Compare commits
82 Commits
1.0.0.1
...
be6c172ec8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be6c172ec8 | ||
| 437403f966 | |||
| dd05d67de6 | |||
| 30978158e9 | |||
| 8519ca3a13 | |||
| 9364a45561 | |||
| b68ae51ddb | |||
| a45651dfbd | |||
| d093cc9007 | |||
| 1a95c0e546 | |||
| 025bed61b1 | |||
|
|
20287a6b8b | ||
|
|
9e0f315b24 | ||
|
|
23c0da2ac8 | ||
|
|
83c6b5071d | ||
|
|
086f278e84 | ||
|
|
e8c0224a03 | ||
|
|
f5a1e14934 | ||
|
|
53ba71f808 | ||
|
|
0984883f68 | ||
|
|
b5cb4ba17f | ||
|
|
d83ac6307e | ||
|
|
f01ba13ad9 | ||
|
|
54b4f2de22 | ||
|
|
51060bcd60 | ||
|
|
fd530878cc | ||
|
|
20267b469e | ||
|
|
68a81020e4 | ||
|
|
7ce3a00613 | ||
|
|
552f7a1f86 | ||
|
|
d1193cba01 | ||
|
|
c35b6b2d72 | ||
|
|
7c339519b2 | ||
|
|
0e4be5305e | ||
|
|
e9ebf82dd1 | ||
|
|
0723f4e9b1 | ||
|
|
dae78d21de | ||
|
|
913d29274e | ||
|
|
fe36953bd5 | ||
|
|
22c94619c6 | ||
|
|
0606cf2216 | ||
|
|
df79e432fc | ||
|
|
22e4534296 | ||
|
|
d8b6c8f488 | ||
|
|
4fcef4d1ef | ||
|
|
0c55aedbb1 | ||
|
|
f5b4834fd5 | ||
|
|
9e40c4e20b | ||
|
|
31dd69dfc5 | ||
|
|
8c10309946 | ||
|
|
7a545224de | ||
|
|
e4e50f76ef | ||
|
|
708728cf62 | ||
|
|
2f19bf5c49 | ||
|
|
ea6a74d967 | ||
|
|
f26c3ed59b | ||
|
|
386059712f | ||
|
|
10a22759eb | ||
|
|
efb03643d1 | ||
|
|
257cb158f4 | ||
|
|
c1dadc777d | ||
|
|
53f27f636e | ||
|
|
baaa6e9452 | ||
|
|
41a2103bdc | ||
|
|
e04f198bc6 | ||
|
|
29090f41a0 | ||
|
|
e6a968e922 | ||
|
|
9bc5cd7540 | ||
|
|
b48a9a3416 | ||
|
|
cfa38c290e | ||
|
|
16c56adb8b | ||
|
|
1de570be42 | ||
|
|
397a8da830 | ||
|
|
f9684f484e | ||
|
|
b12a838a4f | ||
|
|
69f94ae5b0 | ||
|
|
98b9ccb901 | ||
|
|
578a36d3aa | ||
|
|
52eb3e7844 | ||
|
|
3aa20eb50b | ||
|
|
de58df8c60 | ||
|
|
02cd81aab5 |
51
.gitignore
vendored
Normal file
51
.gitignore
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# ---> core
|
||||
*.swp
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
*.pyc
|
||||
nupkg/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode
|
||||
|
||||
# Rider
|
||||
.idea
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
*/[Bb]in/
|
||||
*/[Oo]bj/
|
||||
*/[Oo]ut/
|
||||
|
||||
# Visual Studio 2015
|
||||
.vs/
|
||||
|
||||
# Logs
|
||||
[Ll]og-*.txt
|
||||
[Ll]ogs/
|
||||
|
||||
# Other
|
||||
appsettings.Development.json
|
||||
configurationCache.bin
|
||||
configurationCache.bin.bak
|
||||
*.cache
|
||||
63
AmalgamationTool/AmalgamationTool.csproj
Normal file
63
AmalgamationTool/AmalgamationTool.csproj
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{A64D2052-D0CD-488E-BF05-E5952615D926}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AmalgamationTool</RootNamespace>
|
||||
<AssemblyName>AmalgamationTool</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DynamORM.Amalgamation.cs">
|
||||
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
15085
AmalgamationTool/DynamORM.Amalgamation.cs
Normal file
15085
AmalgamationTool/DynamORM.Amalgamation.cs
Normal file
File diff suppressed because it is too large
Load Diff
192
AmalgamationTool/Program.cs
Normal file
192
AmalgamationTool/Program.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AmalgamationTool
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
List<string> usings = new List<string>();
|
||||
Dictionary<string, List<string>> classes = new Dictionary<string, List<string>>();
|
||||
|
||||
// Build a file using string builder.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (var f in new DirectoryInfo(Path.GetFullPath(args[0].Trim('"', '\''))).GetFiles("*.cs", SearchOption.AllDirectories))
|
||||
{
|
||||
string content = File.ReadAllText(f.FullName);
|
||||
|
||||
string namespaceName = string.Empty;
|
||||
|
||||
// Deal with usings
|
||||
foreach (var u in content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)
|
||||
.Where(l => l.Trim().StartsWith("using ") && !l.Trim().StartsWith("using ("))
|
||||
.Select(l => l.Trim()))
|
||||
if (!usings.Contains(u))
|
||||
usings.Add(u);
|
||||
|
||||
// Extract namespace
|
||||
|
||||
//if (args.Length > 2)
|
||||
//{
|
||||
// var tcontent = Regex.Replace(content, @"^\s*using\s+.*\s*;$", string.Empty);
|
||||
// tcontent = Regex.Replace(content, @"^\s*namespace\s+.*\s*", string.Empty).Trim();
|
||||
|
||||
// var ns = Regex.Match(content, @"^\s*namespace\s+(?<ns>.*)\s*");
|
||||
|
||||
// if (ns.Success)
|
||||
// {
|
||||
// if (!classes.ContainsKey(ns.Groups["ns"].Value))
|
||||
// classes.Add(ns.Groups["ns"].Value, new List<string>());
|
||||
|
||||
// classes[ns.Groups["ns"].Value].Add(tcontent);
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
{
|
||||
if (content.Trim().Length == 0)
|
||||
continue;
|
||||
|
||||
var nstart = content.IndexOf("namespace ") + "namespace ".Length;
|
||||
var bbrace = content.IndexOf("{", nstart);
|
||||
var nlen = bbrace - nstart;
|
||||
|
||||
if (nstart < "namespace ".Length)
|
||||
{
|
||||
if (f.Name.ToLower() == "assemblyinfo.cs")
|
||||
{
|
||||
var hs = content.IndexOf("/*");
|
||||
var es = content.IndexOf("*/", hs) + 2;
|
||||
if (es > hs)
|
||||
{
|
||||
sb.AppendLine(content.Substring(hs, es - hs));
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
string ns = content.Substring(nstart, nlen).Trim();
|
||||
|
||||
// Add namespace if not exist
|
||||
if (!classes.ContainsKey(ns))
|
||||
classes.Add(ns, new List<string>());
|
||||
|
||||
var ebrace = content.LastIndexOf('}');
|
||||
|
||||
// Cut content as class/enum
|
||||
classes[ns].Add(content.Substring(bbrace + 1, ebrace - bbrace - 1));
|
||||
}
|
||||
}
|
||||
|
||||
usings.Sort();
|
||||
|
||||
foreach (var u in usings)
|
||||
sb.AppendLine(u);
|
||||
|
||||
sb.AppendLine(@"
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(""StyleCop.CSharp.MaintainabilityRules"", ""SA1402:FileMayOnlyContainASingleClass"", Justification = ""This is a generated file which generates all the necessary support classes."")]
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(""StyleCop.CSharp.MaintainabilityRules"", ""SA1403:FileMayOnlyContainASingleNamespace"", Justification = ""This is a generated file which generates all the necessary support classes."")]");
|
||||
|
||||
FillClassesAndNamespacesIddented(classes, sb);
|
||||
|
||||
string amalgamation = sb.ToString();
|
||||
|
||||
sb = new StringBuilder();
|
||||
|
||||
string prevTrimmed = null;
|
||||
|
||||
string[] array = amalgamation.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
string l = array[i];
|
||||
var currentTrimmed = l.Trim();
|
||||
var nextTrimmed = (i + 1 == array.Length) ? null : array[i + 1].Trim();
|
||||
|
||||
if (prevTrimmed != null)
|
||||
{
|
||||
switch (prevTrimmed)
|
||||
{
|
||||
case "":
|
||||
if (currentTrimmed == string.Empty)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case "{":
|
||||
case "}":
|
||||
if (currentTrimmed == string.Empty && (nextTrimmed == prevTrimmed || nextTrimmed == string.Empty))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine(l);
|
||||
prevTrimmed = currentTrimmed;
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.GetFullPath(args[1].Trim('"', '\'')), sb.ToString());
|
||||
}
|
||||
|
||||
private static void FillClassesAndNamespaces(Dictionary<string, List<string>> classes, StringBuilder sb)
|
||||
{
|
||||
foreach (var n in classes)
|
||||
{
|
||||
sb.AppendFormat("namespace {0}{1}{{", n.Key, Environment.NewLine);
|
||||
n.Value.ForEach(c => sb.Append(c));
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillClassesAndNamespacesIddented(Dictionary<string, List<string>> classes, StringBuilder sb)
|
||||
{
|
||||
var min = classes.Min(k => k.Key.Split('.').Count());
|
||||
|
||||
foreach (var n in classes.Where(nc => nc.Key.Split('.').Count() == min))
|
||||
{
|
||||
sb.AppendFormat("namespace {0}{1}{{", n.Key, Environment.NewLine);
|
||||
n.Value.ForEach(c => sb.Append(c));
|
||||
|
||||
SubNamespaces(classes, n.Key, sb, min);
|
||||
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SubNamespaces(Dictionary<string, List<string>> classes, string p, StringBuilder sb, int ident)
|
||||
{
|
||||
sb.AppendLine(string.Empty);
|
||||
|
||||
foreach (var n in classes.Where(nc => nc.Key.Split('.').Count() == ident + 1 && nc.Key.StartsWith(p)))
|
||||
{
|
||||
for (int i = 0; i < ident; i++) sb.Append(" ");
|
||||
sb.AppendFormat("namespace {0}{1}", n.Key.Substring(p.Length + 1), Environment.NewLine);
|
||||
|
||||
for (int i = 0; i < ident; i++) sb.Append(" ");
|
||||
sb.Append("{");
|
||||
n.Value.ForEach(c =>
|
||||
{
|
||||
foreach (var l in c.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
|
||||
{
|
||||
for (int i = 0; i < ident; i++) sb.Append(" ");
|
||||
sb.AppendLine(l);
|
||||
}
|
||||
});
|
||||
|
||||
SubNamespaces(classes, n.Key, sb, ident + 1);
|
||||
|
||||
for (int i = 0; i < ident; i++) sb.Append(" ");
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine(string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
AmalgamationTool/Properties/AssemblyInfo.cs
Normal file
36
AmalgamationTool/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("AmalgamationTool")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("AmalgamationTool")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("de540809-dd27-47b1-bb01-7dce3a880cde")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,74 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamORM", "DynamORM\DynamORM.csproj", "{63963ED7-9C78-4672-A4D4-339B6E825503}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamORM.Tests.Mono", "DynamORM.Tests\DynamORM.Tests.Mono.csproj", "{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
StartupItem = DynamORM\DynamORM.csproj
|
||||
Policies = $0
|
||||
$0.DotNetNamingPolicy = $1
|
||||
$1.DirectoryNamespaceAssociation = None
|
||||
$1.ResourceNamePolicy = FileFormatDefault
|
||||
$0.TextStylePolicy = $2
|
||||
$2.FileWidth = 120
|
||||
$2.RemoveTrailingWhitespace = True
|
||||
$2.EolMarker = Windows
|
||||
$2.inheritsSet = VisualStudio
|
||||
$2.inheritsScope = text/plain
|
||||
$2.scope = text/x-csharp
|
||||
$0.CSharpFormattingPolicy = $3
|
||||
$3.inheritsSet = Mono
|
||||
$3.inheritsScope = text/x-csharp
|
||||
$3.scope = text/x-csharp
|
||||
$0.TextStylePolicy = $4
|
||||
$4.FileWidth = 120
|
||||
$4.RemoveTrailingWhitespace = True
|
||||
$4.EolMarker = Windows
|
||||
$4.inheritsSet = VisualStudio
|
||||
$4.inheritsScope = text/plain
|
||||
$4.scope = text/plain
|
||||
$0.TextStylePolicy = $5
|
||||
$5.FileWidth = 120
|
||||
$5.TabWidth = 2
|
||||
$5.RemoveTrailingWhitespace = True
|
||||
$5.EolMarker = Windows
|
||||
$5.inheritsSet = VisualStudio
|
||||
$5.inheritsScope = text/plain
|
||||
$5.scope = text/microsoft-resx
|
||||
$0.XmlFormattingPolicy = $6
|
||||
$6.inheritsSet = null
|
||||
$6.scope = text/microsoft-resx
|
||||
$0.TextStylePolicy = $7
|
||||
$7.FileWidth = 120
|
||||
$7.TabWidth = 2
|
||||
$7.RemoveTrailingWhitespace = True
|
||||
$7.EolMarker = Windows
|
||||
$7.inheritsSet = VisualStudio
|
||||
$7.inheritsScope = text/plain
|
||||
$7.scope = application/xml
|
||||
$0.XmlFormattingPolicy = $8
|
||||
$8.inheritsSet = Mono
|
||||
$8.inheritsScope = application/xml
|
||||
$8.scope = application/xml
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -37,16 +37,18 @@
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="nunit.framework, Version=2.5.10.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
|
||||
<Reference Include="nunit.framework">
|
||||
<Private>False</Private>
|
||||
<Package>nunit</Package>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Data.Sqlite" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Helpers\Dynamic\DynamicParserTests.cs" />
|
||||
<Compile Include="Modify\DynamicModificationTests.cs" />
|
||||
<Compile Include="Modify\DynamicNoSchemaModificationTests.cs" />
|
||||
<Compile Include="Modify\DynamicTypeSchemaModificationTests.cs" />
|
||||
<Compile Include="Modify\ParserTests.cs" />
|
||||
<Compile Include="Select\DynamicNoSchemaAccessTests.cs" />
|
||||
<Compile Include="Select\DynamicTypeSchemaAccessTests.cs" />
|
||||
<Compile Include="Helpers\AttachToDebugger.cs" />
|
||||
@@ -59,6 +61,8 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Select\LegacyParserTests.cs" />
|
||||
<Compile Include="Select\ParserTests.cs" />
|
||||
<Compile Include="Select\RenamedTypedAccessTests.cs" />
|
||||
<Compile Include="TestsBase.cs" />
|
||||
<Compile Include="Select\TypedAccessTests.cs" />
|
||||
@@ -84,4 +88,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -10,47 +10,82 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DynamORM.Tests</RootNamespace>
|
||||
<AssemblyName>DynamORM.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
|
||||
<NoStdLib>False</NoStdLib>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||
<SignAssembly>False</SignAssembly>
|
||||
<DelaySign>False</DelaySign>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DebugType>Full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<BaseAddress>4194304</BaseAddress>
|
||||
<RegisterForComInterop>False</RegisterForComInterop>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<Prefer32Bit>False</Prefer32Bit>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<DebugType>PdbOnly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<BaseAddress>4194304</BaseAddress>
|
||||
<RegisterForComInterop>False</RegisterForComInterop>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<Prefer32Bit>False</Prefer32Bit>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
|
||||
<BaseAddress>4194304</BaseAddress>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<RegisterForComInterop>False</RegisterForComInterop>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<Prefer32Bit>False</Prefer32Bit>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<HintPath>C:\Program Files (x86)\NUnit 2.6.2\bin\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>Libraries\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Helpers\Dynamic\DynamicParserTests.cs" />
|
||||
<Compile Include="Helpers\PoolingTests.cs" />
|
||||
<Compile Include="Helpers\Validation\ObjectValidationTest.cs" />
|
||||
<Compile Include="Modify\DynamicModificationTests.cs" />
|
||||
<Compile Include="Modify\DynamicNoSchemaModificationTests.cs" />
|
||||
<Compile Include="Modify\DynamicTypeSchemaModificationTests.cs" />
|
||||
<Compile Include="Modify\ParserTests.cs" />
|
||||
<Compile Include="Select\DynamicNoSchemaAccessTests.cs" />
|
||||
<Compile Include="Select\DynamicTypeSchemaAccessTests.cs" />
|
||||
<Compile Include="Helpers\AttachToDebugger.cs" />
|
||||
@@ -63,7 +98,8 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Select\RenamedTypedAccessTests.cs" />
|
||||
<Compile Include="Select\LegacyParserTests.cs" />
|
||||
<Compile Include="Select\ParserTests.cs" />
|
||||
<Compile Include="TestsBase.cs" />
|
||||
<Compile Include="Select\TypedAccessTests.cs" />
|
||||
</ItemGroup>
|
||||
@@ -71,6 +107,7 @@
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -79,6 +116,17 @@
|
||||
<Name>DynamORM</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest.TestAdapter">
|
||||
<Version>3.3.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestFramework">
|
||||
<Version>3.3.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Data.SQLite">
|
||||
<Version>1.0.118</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
108
DynamORM.Tests/DynamicClassBuilderTest.cs
Normal file
108
DynamORM.Tests/DynamicClassBuilderTest.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace DynamORM.Tests
|
||||
{
|
||||
public class DynamicProduct : IDictionary
|
||||
{
|
||||
private IDictionary _dict = null;
|
||||
|
||||
public DynamicProduct(IDictionary dict)
|
||||
{
|
||||
_dict = dict;
|
||||
}
|
||||
|
||||
// Properties from dict
|
||||
public int ID
|
||||
{
|
||||
get { return (int)(_dict["ID"] ?? 0); }
|
||||
set
|
||||
{
|
||||
if (!IsReadOnly)
|
||||
_dict["ID"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return (string)(_dict["Name"] ?? 0); }
|
||||
set
|
||||
{
|
||||
if (!IsReadOnly)
|
||||
_dict["Name"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime Delivery
|
||||
{
|
||||
get { return (DateTime)(_dict["Delivery"] ?? 0); }
|
||||
set
|
||||
{
|
||||
if (!IsReadOnly)
|
||||
_dict["Delivery"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public object Data
|
||||
{
|
||||
get { return (object)(_dict["Data"] ?? 0); }
|
||||
set
|
||||
{
|
||||
if (!IsReadOnly)
|
||||
_dict["Data"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// IDictionary implementation
|
||||
public void Add(object key, object value)
|
||||
{
|
||||
_dict.Add(key, value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_dict.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(object key)
|
||||
{
|
||||
return _dict.Contains(key);
|
||||
}
|
||||
|
||||
public IDictionaryEnumerator GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Remove(object key)
|
||||
{
|
||||
_dict.Remove(key);
|
||||
}
|
||||
|
||||
public void CopyTo(System.Array array, int index)
|
||||
{
|
||||
_dict.CopyTo(array, index);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _dict.GetEnumerator();
|
||||
}
|
||||
|
||||
public bool IsFixedSize { get { return _dict.IsFixedSize; } }
|
||||
|
||||
public bool IsReadOnly { get { return _dict.IsReadOnly; } }
|
||||
|
||||
public ICollection Keys { get { return _dict.Keys; } }
|
||||
|
||||
public ICollection Values { get { return _dict.Values; } }
|
||||
|
||||
public object this[object key] { get { return _dict[key]; } set { _dict[key] = value; } }
|
||||
|
||||
public int Count { get { return _dict.Count; } }
|
||||
|
||||
public bool IsSynchronized { get { return _dict.IsSynchronized; } }
|
||||
|
||||
public object SyncRoot { get { return _dict.SyncRoot; } }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,25 +28,16 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Helpers
|
||||
{
|
||||
/// <summary>Class responsible for users operations testing.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class AttachToDebugger
|
||||
{
|
||||
/// <summary>Attach to debugger.</summary>
|
||||
[Test]
|
||||
[Explicit("Test for attaching debugger to NUnit test framework")]
|
||||
public void Attach()
|
||||
{
|
||||
if (!Debugger.IsAttached)
|
||||
Debugger.Launch();
|
||||
}
|
||||
|
||||
/// <summary>Test anonymous type compatibility.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestAnonType()
|
||||
{
|
||||
var a = new { x = 1, y = 2 };
|
||||
@@ -56,7 +47,7 @@ namespace DynamORM.Tests.Helpers
|
||||
}
|
||||
|
||||
/// <summary>Test anonymous type value.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestAnonTypeValue()
|
||||
{
|
||||
var a = new { x = 1, y = "bla bla" };
|
||||
|
||||
121
DynamORM.Tests/Helpers/Dynamic/DynamicParserTests.cs
Normal file
121
DynamORM.Tests/Helpers/Dynamic/DynamicParserTests.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Helpers.Dynamic
|
||||
{
|
||||
/// <summary><see cref="DynamicParser"/> tests.</summary>
|
||||
[TestClass]
|
||||
public class DynamicParserTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests the get member.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestGetMember()
|
||||
{
|
||||
Func<dynamic, object> f = x => x.SomePropery;
|
||||
|
||||
var val = DynamicParser.Parse(f).Result as DynamicParser.Node.GetMember;
|
||||
|
||||
Assert.IsNotNull(val);
|
||||
Assert.AreEqual("SomePropery", val.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the set member.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSetMember()
|
||||
{
|
||||
Func<dynamic, object> f = x => x.SomePropery = "value";
|
||||
|
||||
var val = DynamicParser.Parse(f).Result as DynamicParser.Node.SetMember;
|
||||
|
||||
Assert.IsNotNull(val);
|
||||
Assert.AreEqual("SomePropery", val.Name);
|
||||
Assert.AreEqual("value", val.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the index of the get.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestGetIndex()
|
||||
{
|
||||
Func<dynamic, object> f = x => x.SomePropery[0];
|
||||
|
||||
var val = DynamicParser.Parse(f).Result as DynamicParser.Node.GetIndex;
|
||||
|
||||
Assert.IsNotNull(val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the index of the set.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSetIndex()
|
||||
{
|
||||
Func<dynamic, object> f = x => x.SomePropery[0] = "value";
|
||||
|
||||
var val = DynamicParser.Parse(f).Result as DynamicParser.Node.SetIndex;
|
||||
|
||||
Assert.IsNotNull(val);
|
||||
Assert.AreEqual("value", val.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests something.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSomething()
|
||||
{
|
||||
Func<dynamic, object> f = x => x.SomePropery == "value" || x.OtherProperty == -1;
|
||||
|
||||
var p = DynamicParser.Parse(f);
|
||||
var val = p.Result as DynamicParser.Node.Binary;
|
||||
|
||||
Assert.IsNotNull(val);
|
||||
|
||||
var left = val.Host as DynamicParser.Node.Binary;
|
||||
var right = val.Right as DynamicParser.Node.Binary;
|
||||
|
||||
Assert.IsNotNull(left);
|
||||
Assert.IsNotNull(right);
|
||||
|
||||
Assert.IsInstanceOfType(left.Host, typeof(DynamicParser.Node.GetMember));
|
||||
Assert.IsInstanceOfType(right.Host, typeof(DynamicParser.Node.GetMember));
|
||||
|
||||
Assert.AreEqual("value", left.Right);
|
||||
Assert.AreEqual(-1, right.Right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,24 +26,23 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Helpers
|
||||
{
|
||||
/// <summary>Pooling tests.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class PoolingTests : TestsBase
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestFixtureSetUp]
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestFixtureTearDown]
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
@@ -51,7 +50,7 @@ namespace DynamORM.Tests.Helpers
|
||||
}
|
||||
|
||||
/// <summary>Test single mode command disposing.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestSingleModeCommand()
|
||||
{
|
||||
CreateDynamicDatabase();
|
||||
@@ -63,11 +62,11 @@ namespace DynamORM.Tests.Helpers
|
||||
Database.Dispose();
|
||||
Database = null;
|
||||
|
||||
Assert.Throws<ObjectDisposedException>(() => cmd.ExecuteScalar());
|
||||
Assert.ThrowsException<DynamicQueryException>(() => cmd.ExecuteScalar());
|
||||
}
|
||||
|
||||
/// <summary>Test single mode transaction disposing.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestSingleModeTransaction()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
80
DynamORM.Tests/Helpers/Validation/ObjectValidationTest.cs
Normal file
80
DynamORM.Tests/Helpers/Validation/ObjectValidationTest.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using DynamORM.Mapper;
|
||||
using DynamORM.Validation;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Helpers.Validation
|
||||
{
|
||||
[TestClass]
|
||||
public class ObjectValidationTest
|
||||
{
|
||||
public class TestObject
|
||||
{
|
||||
[Required(1f, 10f)]
|
||||
public int TestInt { get; set; }
|
||||
|
||||
[Required(7, false, false)]
|
||||
public string CanBeNull { get; set; }
|
||||
|
||||
[Required(2, true)]
|
||||
[Required(7, 18, ElementRequirement = true)]
|
||||
public decimal[] ArrayTest { get; set; }
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidateCorrectObject()
|
||||
{
|
||||
var result = DynamicMapperCache.GetMapper<TestObject>().ValidateObject(
|
||||
new TestObject
|
||||
{
|
||||
TestInt = 2,
|
||||
ArrayTest = new decimal[] { 7, 18 },
|
||||
});
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(0, result.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidateIncorrectObject()
|
||||
{
|
||||
var result = DynamicMapperCache.GetMapper<TestObject>().ValidateObject(
|
||||
new TestObject
|
||||
{
|
||||
TestInt = 0,
|
||||
CanBeNull = string.Empty,
|
||||
ArrayTest = new decimal[] { 0, 0 },
|
||||
});
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(4, result.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,16 +28,16 @@
|
||||
|
||||
using System;
|
||||
using DynamORM.Tests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Modify
|
||||
{
|
||||
/// <summary>Test standard dynamic access ORM.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class DynamicModificationTests : TestsBase
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestFixtureSetUp]
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
@@ -45,7 +45,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestFixtureTearDown]
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
@@ -62,14 +62,14 @@ namespace DynamORM.Tests.Modify
|
||||
#region Insert
|
||||
|
||||
/// <summary>Test row insertion by dynamic arguments.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestInsertByArguments()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Insert(code: "201", first: null, last: "Gagarin", email: "juri.gagarin@megacorp.com", quote: "bla, bla, bla"));
|
||||
|
||||
// Verify
|
||||
var o = GetTestTable().Single(code: "201");
|
||||
Assert.Less(200, o.id);
|
||||
Assert.AreNotEqual(200, o.id);
|
||||
Assert.AreEqual("201", o.code.ToString());
|
||||
Assert.AreEqual(null, o.first);
|
||||
Assert.AreEqual("Gagarin", o.last);
|
||||
@@ -79,14 +79,14 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row insertion by dynamic object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestInsertByDynamicObjects()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Insert(values: new { code = "202", first = DBNull.Value, last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }));
|
||||
|
||||
// Verify
|
||||
var o = GetTestTable().Single(code: "202");
|
||||
Assert.Less(200, o.id);
|
||||
Assert.AreNotEqual(200, o.id);
|
||||
Assert.AreEqual("202", o.code.ToString());
|
||||
Assert.AreEqual(null, o.first);
|
||||
Assert.AreEqual("Gagarin", o.last);
|
||||
@@ -96,7 +96,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row insertion by mapped object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestInsertByMappedObject()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -113,7 +113,7 @@ namespace DynamORM.Tests.Modify
|
||||
|
||||
// Verify
|
||||
var o = u.Single(code: "203");
|
||||
Assert.Less(200, o.id);
|
||||
Assert.AreNotEqual(200, o.id);
|
||||
Assert.AreEqual("203", o.code.ToString());
|
||||
Assert.AreEqual(null, o.first);
|
||||
Assert.AreEqual("Gagarin", o.last);
|
||||
@@ -123,7 +123,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row insertion by basic object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestInsertByBasicObject()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -140,7 +140,7 @@ namespace DynamORM.Tests.Modify
|
||||
|
||||
// Verify
|
||||
var o = u.Single(code: "204");
|
||||
Assert.Less(200, o.id);
|
||||
Assert.AreNotEqual(200, o.id);
|
||||
Assert.AreEqual("204", o.code.ToString());
|
||||
Assert.AreEqual(null, o.first);
|
||||
Assert.AreEqual("Gagarin", o.last);
|
||||
@@ -154,7 +154,7 @@ namespace DynamORM.Tests.Modify
|
||||
#region Update
|
||||
|
||||
/// <summary>Test row updating by dynamic arguments.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByArguments()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Update(id: 1, code: "201", first: null, last: "Gagarin", email: "juri.gagarin@megacorp.com", quote: "bla, bla, bla"));
|
||||
@@ -171,7 +171,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row updating by dynamic objects.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByDynamicObject()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Update(update: new { id = 2, code = "202", first = DBNull.Value, last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }));
|
||||
@@ -188,7 +188,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row updating by mapped object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByMappedObject()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -215,7 +215,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row updating by basic object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByBasicObject()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -242,7 +242,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row updating by dynamic objects.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByDynamicObjects()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Update(values: new { code = "205", first = DBNull.Value, last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }, where: new { id = 5 }));
|
||||
@@ -259,7 +259,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row updating by mapped objects.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByMappedObjects()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -286,7 +286,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row updating by basic objects.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUpdateByBasicObjects()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -317,7 +317,7 @@ namespace DynamORM.Tests.Modify
|
||||
#region Delete
|
||||
|
||||
/// <summary>Test row deleting by dynamic arguments.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestDeleteByArguments()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Delete(code: "10"));
|
||||
@@ -327,7 +327,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row deleting by dynamic objects (all except ID should be ignored).</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestDeleteyDynamicObject()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Delete(delete: new { id = 11, code = 11, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }));
|
||||
@@ -337,7 +337,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row deleting by mapped object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestDeleteByMappedObject()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -357,7 +357,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row deleting by basic object.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestDeleteByBasicObject()
|
||||
{
|
||||
var u = GetTestTable();
|
||||
@@ -377,7 +377,7 @@ namespace DynamORM.Tests.Modify
|
||||
}
|
||||
|
||||
/// <summary>Test row deleting by dynamic objects (all except ID should be ignored).</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestDeleteyDynamicObjectWhere()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Delete(where: new { id = 14, code = "14" }));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,17 +26,17 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Modify
|
||||
{
|
||||
/// <summary>Test standard dynamic access ORM. With out schema information from database.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class DynamicNoSchemaModificationTests : DynamicModificationTests
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestFixtureSetUp]
|
||||
public override void SetUp()
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
CreateDynamicDatabase(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,13 +26,14 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Tests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Modify
|
||||
{
|
||||
/// <summary>Test standard dynamic access ORM. With out schema information from database.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class DynamicTypeSchemaModificationTests : DynamicModificationTests
|
||||
{
|
||||
/// <summary>Create table using specified method.</summary>
|
||||
@@ -41,5 +42,28 @@ namespace DynamORM.Tests.Modify
|
||||
{
|
||||
return Database.Table<users>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the bulk insert.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestBulkInsert()
|
||||
{
|
||||
Assert.AreEqual(2, Database.Insert<users>(new List<users>
|
||||
{
|
||||
new users
|
||||
{
|
||||
id = 1001,
|
||||
login = "a",
|
||||
},
|
||||
new users
|
||||
{
|
||||
id = 1002,
|
||||
login = "b",
|
||||
}
|
||||
}));
|
||||
|
||||
Assert.AreEqual(2, Database.Delete<users>().Where(u => u.users.id.In(1001, 1002)).Execute());
|
||||
}
|
||||
}
|
||||
}
|
||||
206
DynamORM.Tests/Modify/ParserTests.cs
Normal file
206
DynamORM.Tests/Modify/ParserTests.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using DynamORM.Tests.Helpers;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DynamORM.Tests.Modify
|
||||
{
|
||||
/// <summary>New parser tests.</summary>
|
||||
[TestClass]
|
||||
public class ParserTests : TestsBase
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
CreateDynamicDatabase(
|
||||
DynamicDatabaseOptions.SingleConnection |
|
||||
DynamicDatabaseOptions.SingleTransaction |
|
||||
DynamicDatabaseOptions.SupportLimitOffset);
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
DestroyTestDatabase();
|
||||
}
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <summary>
|
||||
/// Tests the basic insert.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInsertBasic()
|
||||
{
|
||||
IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Values(x => x.Users.Code = "001", x => x.Users.Name = "Admin", x => x.Users.IsAdmin = 1);
|
||||
|
||||
Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0})",
|
||||
string.Join(", ", cmd.Parameters.Keys.Select(p => string.Format("[${0}]", p)))), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the insert with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInsertSubQuery()
|
||||
{
|
||||
IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Values(x => x.Code = "001", x => x.Name = "Admin", x => x.IsAdmin = x(cmd
|
||||
.SubQuery(a => a.AccessRights.As(a.a))
|
||||
.Select(a => a.IsAdmin)
|
||||
.Where(a => a.User_Id == "001")));
|
||||
|
||||
Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0}, (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = [${1}])))",
|
||||
string.Join(", ", cmd.Parameters.Keys.Take(2).Select(p => string.Format("[${0}]", p))), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the basic insert using object.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInsertBasicObject()
|
||||
{
|
||||
IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Values(x => new { Code = "001", Name = "Admin", IsAdmin = 1 });
|
||||
|
||||
Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0})",
|
||||
string.Join(", ", cmd.Parameters.Keys.Select(p => string.Format("[${0}]", p)))), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the insert using object with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInsertSubQueryObject()
|
||||
{
|
||||
IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Values(x => new
|
||||
{
|
||||
Code = "001",
|
||||
Name = "Admin",
|
||||
IsAdmin = x(cmd
|
||||
.SubQuery(a => a.AccessRights.As(a.a))
|
||||
.Select(a => a.IsAdmin)
|
||||
.Where(a => a.User_Id == "001"))
|
||||
});
|
||||
|
||||
Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0}, (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = [${1}])))",
|
||||
string.Join(", ", cmd.Parameters.Keys.Take(2).Select(p => string.Format("[${0}]", p))), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
#endregion Insert
|
||||
|
||||
#region Update
|
||||
|
||||
/// <summary>
|
||||
/// Tests the basic update.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestUpdateBasic()
|
||||
{
|
||||
IDynamicUpdateQueryBuilder cmd = new DynamicUpdateQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Set(x => x.Users.Code = "001", x => x.Users.Name = "Admin", x => x.Users.IsAdmin = 1)
|
||||
.Where(x => x.Users.Id_User == 1);
|
||||
|
||||
Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = [${2}] WHERE (""Users"".""Id_User"" = [${3}])",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], cmd.Parameters.Keys.ToArray()[3]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the insert with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestUpdateSubQuery()
|
||||
{
|
||||
IDynamicUpdateQueryBuilder cmd = new DynamicUpdateQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Set(x => x.Users.Code = "001", x => x.Users.Name = "Admin", x => x.Users.IsAdmin = x(cmd
|
||||
.SubQuery(a => a.AccessRights.As(a.a))
|
||||
.Select(a => a.IsAdmin)
|
||||
.Where(a => a.User_Id == a.Users.Id_User)))
|
||||
.Where(x => x.Users.Id_User == 1);
|
||||
|
||||
Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = ""Users"".""Id_User"")) WHERE (""Users"".""Id_User"" = [${2}])",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the basic insert using object.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestUpdateBasicObject()
|
||||
{
|
||||
IDynamicUpdateQueryBuilder cmd = new DynamicUpdateQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Set(x => new { Code = "001", Name = "Admin", IsAdmin = 1 })
|
||||
.Where(x => new { Id_User = 1 });
|
||||
|
||||
Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = [${2}] WHERE (""Id_User"" = [${3}])",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], cmd.Parameters.Keys.ToArray()[3]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the basic insert using object.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestUpdateSubQueryObject()
|
||||
{
|
||||
IDynamicUpdateQueryBuilder cmd = new DynamicUpdateQueryBuilder(Database, "Users");
|
||||
|
||||
cmd.Set(x => new
|
||||
{
|
||||
Code = "001",
|
||||
Name = "Admin",
|
||||
IsAdmin = x(cmd
|
||||
.SubQuery(a => a.AccessRights.As(a.a))
|
||||
.Select(a => a.IsAdmin)
|
||||
.Where(a => a.User_Id == a.Users.Id_User))
|
||||
}).Where(x => new { Id_User = 1 });
|
||||
|
||||
Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = ""Users"".""Id_User"")) WHERE (""Id_User"" = [${2}])",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
#endregion Update
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -61,5 +61,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.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.1")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.1")]
|
||||
[assembly: AssemblyVersion("1.1.0.1")]
|
||||
[assembly: AssemblyFileVersion("1.1.0.1")]
|
||||
4
DynamORM.Tests/Properties/Resources.Designer.cs
generated
4
DynamORM.Tests/Properties/Resources.Designer.cs
generated
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.269
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -19,7 +19,7 @@ namespace DynamORM.Tests.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -29,16 +29,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using DynamORM.Builders;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>Test standard dynamic access ORM.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class DynamicAccessTests : TestsBase
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestFixtureSetUp]
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
@@ -46,7 +47,7 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestFixtureTearDown]
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
@@ -60,24 +61,38 @@ namespace DynamORM.Tests.Select
|
||||
return Database.Table("users");
|
||||
}
|
||||
|
||||
/// <summary>Create table using specified method.</summary>
|
||||
/// <returns>Dynamic table.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder GetTestBuilder()
|
||||
{
|
||||
return Database.Table("users").Query() as IDynamicSelectQueryBuilder;
|
||||
}
|
||||
|
||||
#region Select
|
||||
|
||||
/// <summary>Test unknown op.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestUnknownOperation()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => GetTestTable().MakeMeASandwitch(with: "cheese"));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => GetTestTable().MakeMeASandwitch(with: "cheese"));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Count</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestCount()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Count(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in steatement.</summary>
|
||||
[Test]
|
||||
/// <summary>Test dynamic <c>Count</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestCount2()
|
||||
{
|
||||
Assert.AreEqual(200L, GetTestBuilder().Select(x => x.Count(x.id)).Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public void TestSelectInEnumerableCount()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestTable().Count(last: new DynamicColumn
|
||||
@@ -87,8 +102,18 @@ namespace DynamORM.Tests.Select
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in steatement.</summary>
|
||||
[Test]
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public void TestSelectInEnumerableCount2()
|
||||
{
|
||||
Assert.AreEqual(4L, GetTestBuilder()
|
||||
.Where(x => x.last.In(new object[] { "Hendricks", "Goodwin", "Freeman" }.Take(3)))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public void TestSelectInArrayCount()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestTable().Count(last: new DynamicColumn
|
||||
@@ -98,71 +123,157 @@ namespace DynamORM.Tests.Select
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public void TestSelectInArrayCount2()
|
||||
{
|
||||
Assert.AreEqual(4L, GetTestBuilder()
|
||||
.Where(x => x.last.In(new object[] { "Hendricks", "Goodwin", "Freeman" }))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>First</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestFirst()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().First(columns: "id").id);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>First</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestFirst2()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestBuilder()
|
||||
.Select(x => x.id)
|
||||
.Execute()
|
||||
.First().id);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Last</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestLast()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Last(columns: "id").id);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Last</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestLast2()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestBuilder()
|
||||
.Select(x => x.id)
|
||||
.Execute()
|
||||
.Last().id);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Count</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestCountSpecificRecord()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Count(first: "Ori"));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Count</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestCountSpecificRecord2()
|
||||
{
|
||||
Assert.AreEqual(1L, GetTestBuilder()
|
||||
.Where(x => x.first == "Ori")
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestMin()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Min(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestMin2()
|
||||
{
|
||||
Assert.AreEqual(1L, GetTestBuilder()
|
||||
.Select(x => x.Min(x.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Min</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestMax()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Max(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestMax2()
|
||||
{
|
||||
Assert.AreEqual(200L, GetTestBuilder()
|
||||
.Select(x => x.Max(x.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Min</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TesttAvg()
|
||||
{
|
||||
Assert.AreEqual(100.5, GetTestTable().Avg(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Min</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TesttAvg2()
|
||||
{
|
||||
Assert.AreEqual(100.5, GetTestBuilder()
|
||||
.Select(x => x.Avg(x.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Sum</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestSum()
|
||||
{
|
||||
Assert.AreEqual(20100, GetTestTable().Sum(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Sum</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestSum2()
|
||||
{
|
||||
Assert.AreEqual(20100L, GetTestBuilder()
|
||||
.Select(x => x.Sum(x.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method for invalid operation exception.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestScalarException()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => GetTestTable().Scalar(id: 19));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => GetTestTable().Scalar(id: 19));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestScalar()
|
||||
{
|
||||
Assert.AreEqual("Ori", GetTestTable().Scalar(columns: "first", id: 19));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestScalar2()
|
||||
{
|
||||
Assert.AreEqual("Ori", GetTestBuilder()
|
||||
.Where(x => x.id == 19)
|
||||
.Select(x => x.first)
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method with SQLite specific aggregate.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestScalarGroupConcat()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
@@ -171,8 +282,21 @@ namespace DynamORM.Tests.Select
|
||||
GetTestTable().Scalar(columns: "first:first:group_concat", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 }));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method with SQLite specific aggregate.</summary>
|
||||
[TestMethod]
|
||||
public void TestScalarGroupConcat2()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
// select group_concat("first") AS first from "users" where "id" < 20;
|
||||
Assert.AreEqual("Clarke,Marny,Dai,Forrest,Blossom,George,Ivory,Inez,Sigourney,Fulton,Logan,Anne,Alexandra,Adena,Lionel,Aimee,Selma,Lara,Ori",
|
||||
GetTestBuilder()
|
||||
.Where(x => x.id < 20)
|
||||
.Select(x => x.group_concat(x.first).As(x.first))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method with SQLite specific aggregate not using aggregate field.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestScalarGroupConcatNoAggregateField()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
@@ -181,8 +305,21 @@ namespace DynamORM.Tests.Select
|
||||
GetTestTable().Scalar(columns: "group_concat(first):first", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 }));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Scalar</c> method with SQLite specific aggregate not using aggregate field.</summary>
|
||||
[TestMethod]
|
||||
public void TestScalarGroupConcatNoAggregateField2()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
// select group_concat(first) AS first from "users" where "id" < 20;
|
||||
Assert.AreEqual("Clarke,Marny,Dai,Forrest,Blossom,George,Ivory,Inez,Sigourney,Fulton,Logan,Anne,Alexandra,Adena,Lionel,Aimee,Selma,Lara,Ori",
|
||||
GetTestBuilder()
|
||||
.Where(x => x.id < 20)
|
||||
.SelectColumn("group_concat(first):first")
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test something fancy... like: <code>select "first", count("first") occurs from "users" group by "first" order by 2 desc;</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestFancyAggregateQuery()
|
||||
{
|
||||
var v = (GetTestTable().Query(columns: "first,first:occurs:count", group: "first", order: ":desc:2") as IEnumerable<dynamic>).ToList();
|
||||
@@ -196,20 +333,58 @@ namespace DynamORM.Tests.Select
|
||||
Assert.AreEqual(1, v.Last().occurs);
|
||||
}
|
||||
|
||||
/// <summary>Test something fancy... like: <code>select "first", count("first") occurs from "users" group by "first" order by 2 desc;</code>.</summary>
|
||||
[TestMethod]
|
||||
public void TestFancyAggregateQuery2()
|
||||
{
|
||||
var v = GetTestBuilder()
|
||||
.Select(x => x.first, x => x.Count(x.first).As(x.occurs))
|
||||
.GroupBy(x => x.first)
|
||||
.OrderBy(x => x.Desc(2))
|
||||
.Execute()
|
||||
.ToList();
|
||||
|
||||
Assert.IsNotNull(v);
|
||||
Assert.AreEqual(187, v.Count());
|
||||
Assert.AreEqual(4, v.First().occurs);
|
||||
Assert.AreEqual("Logan", v.First().first);
|
||||
Assert.AreEqual(2, v.Take(10).Last().occurs);
|
||||
Assert.AreEqual(1, v.Take(11).Last().occurs);
|
||||
Assert.AreEqual(1, v.Last().occurs);
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("login")) len from "users";</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestAggregateInAggregate()
|
||||
{
|
||||
Assert.AreEqual(12.77, GetTestTable().Scalar(columns: @"length(""login""):len:avg"));
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("login")) len from "users";</code>.</summary>
|
||||
[TestMethod]
|
||||
public void TestAggregateInAggregate2()
|
||||
{
|
||||
Assert.AreEqual(12.77, GetTestBuilder()
|
||||
.Select(x => x.Avg(x.Length(x.login)).As(x.len))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("email")) len from "users";</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestAggregateInAggregateMark2()
|
||||
{
|
||||
Assert.AreEqual(27.7, GetTestTable().Avg(columns: @"length(""email""):len"));
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("email")) len from "users";</code>.</summary>
|
||||
[TestMethod]
|
||||
public void TestAggregateInAggregateMark3()
|
||||
{
|
||||
Assert.AreEqual(27.7, GetTestBuilder()
|
||||
.Select(x => "AVG(LENGTH(email)) AS LEN")
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test emails longer than 27 chars. <code>select count(*) from "users" where length("email") > 27;</code>.</summary>
|
||||
public void TestFunctionInWhere()
|
||||
{
|
||||
@@ -224,8 +399,17 @@ namespace DynamORM.Tests.Select
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>Test emails longer than 27 chars. <code>select count(*) from "users" where length("email") > 27;</code>.</summary>
|
||||
public void TestFunctionInWhere2()
|
||||
{
|
||||
Assert.AreEqual(97, GetTestBuilder()
|
||||
.Where(x => x.Length(x.email) > 27)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Single</c> multi.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestSingleObject()
|
||||
{
|
||||
var exp = new { id = 19, first = "Ori", last = "Ellis" };
|
||||
@@ -236,87 +420,236 @@ namespace DynamORM.Tests.Select
|
||||
Assert.AreEqual(exp.last, o.last);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic <c>Single</c> multi.</summary>
|
||||
[TestMethod]
|
||||
public void TestSingleObject2()
|
||||
{
|
||||
var exp = new { id = 19, first = "Ori", last = "Ellis" };
|
||||
var o = GetTestBuilder()
|
||||
.Where(x => x.id == 19)
|
||||
.Select(x => new { id = x.id, first = x.first, last = x.last })
|
||||
.Execute()
|
||||
.First();
|
||||
|
||||
Assert.AreEqual(exp.id, o.id);
|
||||
Assert.AreEqual(exp.first, o.first);
|
||||
Assert.AreEqual(exp.last, o.last);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic duplicate column name occurrence.</summary>
|
||||
[TestMethod]
|
||||
public void TestDuplicateColumnNameException()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => GetTestBuilder()
|
||||
.Where(x => x.id == 19)
|
||||
.Select(x => new
|
||||
{
|
||||
id = x.id,
|
||||
first = x.first,
|
||||
last = x.last,
|
||||
})
|
||||
.Select(x => x.last.As(x.first)) // Make last be first
|
||||
.Execute()
|
||||
.First());
|
||||
}
|
||||
|
||||
#endregion Select
|
||||
|
||||
#region Where
|
||||
|
||||
/// <summary>Test dynamic where expression equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereEq()
|
||||
{
|
||||
Assert.AreEqual("hoyt.tran", GetTestTable().Single(where: new DynamicColumn("id").Eq(100)).login);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression equal.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereEq2()
|
||||
{
|
||||
Assert.AreEqual("hoyt.tran", GetTestBuilder()
|
||||
.Where(x => x.id == 100).Execute().First().login);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression not equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereNot()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestTable().Count(where: new DynamicColumn("id").Not(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression not equal.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNot2()
|
||||
{
|
||||
Assert.AreEqual(199L, GetTestBuilder()
|
||||
.Where(x => x.id != 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression like.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereLike()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Single(where: new DynamicColumn("login").Like("Hoyt.%")).id);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression like.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereLike2()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestBuilder()
|
||||
.Where(x => x.login.Like("Hoyt.%")).Execute().First().id);
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression not like.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereNotLike()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestTable().Count(where: new DynamicColumn("login").NotLike("Hoyt.%")));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression not like.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNotLike2()
|
||||
{
|
||||
Assert.AreEqual(199L, GetTestBuilder()
|
||||
.Where(x => x.login.NotLike("Hoyt.%"))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression not like.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNotLike3()
|
||||
{
|
||||
Assert.AreEqual(199L, GetTestBuilder()
|
||||
.Where(x => !x.login.Like("Hoyt.%"))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression greater.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereGt()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Count(where: new DynamicColumn("id").Greater(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression greater.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereGt2()
|
||||
{
|
||||
Assert.AreEqual(100L, GetTestBuilder()
|
||||
.Where(x => x.id > 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression greater or equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereGte()
|
||||
{
|
||||
Assert.AreEqual(101, GetTestTable().Count(where: new DynamicColumn("id").GreaterOrEqual(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression greater or equal.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereGte2()
|
||||
{
|
||||
Assert.AreEqual(101L, GetTestBuilder()
|
||||
.Where(x => x.id >= 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression less.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereLt()
|
||||
{
|
||||
Assert.AreEqual(99, GetTestTable().Count(where: new DynamicColumn("id").Less(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression less.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereLt2()
|
||||
{
|
||||
Assert.AreEqual(99L, GetTestBuilder()
|
||||
.Where(x => x.id < 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression less or equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereLte()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Count(where: new DynamicColumn("id").LessOrEqual(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression less or equal.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereLte2()
|
||||
{
|
||||
Assert.AreEqual(100L, GetTestBuilder()
|
||||
.Where(x => x.id <= 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression between.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereBetween()
|
||||
{
|
||||
Assert.AreEqual(26, GetTestTable().Count(where: new DynamicColumn("id").Between(75, 100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression in params.</summary>
|
||||
[Test]
|
||||
/// <summary>Test dynamic where expression between.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereBetween2()
|
||||
{
|
||||
Assert.AreEqual(26L, GetTestBuilder()
|
||||
.Where(x => x.id.Between(75, 100))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression in parameters.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereIn1()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestTable().Count(where: new DynamicColumn("id").In(75, 99, 100)));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression in array.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public void TestWhereIn2()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestTable().Count(where: new DynamicColumn("id").In(new[] { 75, 99, 100 })));
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression in parameters.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereIn3()
|
||||
{
|
||||
Assert.AreEqual(3L, GetTestBuilder()
|
||||
.Where(x => x.id.In(75, 99, 100))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test dynamic where expression in parameters.</summary>
|
||||
[TestMethod]
|
||||
public void TestWhereIn4()
|
||||
{
|
||||
Assert.AreEqual(3L, GetTestBuilder()
|
||||
.Where(x => x.id.In(new[] { 75, 99, 100 }))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,16 +26,16 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>Test standard dynamic access ORM. With out schema information from database.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class DynamicNoSchemaAccessTests : DynamicAccessTests
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestFixtureSetUp]
|
||||
[TestInitialize]
|
||||
public override void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,12 +27,12 @@
|
||||
*/
|
||||
|
||||
using DynamORM.Tests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>Test standard dynamic access ORM. With out schema information from database.</summary>
|
||||
[TestFixture]
|
||||
[TestClass]
|
||||
public class DynamicTypeSchemaAccessTests : DynamicNoSchemaAccessTests
|
||||
{
|
||||
/// <summary>Create table using specified method.</summary>
|
||||
|
||||
321
DynamORM.Tests/Select/LegacyParserTests.cs
Normal file
321
DynamORM.Tests/Select/LegacyParserTests.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>Tests of legacy parser methods.</summary>
|
||||
[TestClass]
|
||||
public class LegacyParserTests : TestsBase
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
CreateDynamicDatabase(
|
||||
DynamicDatabaseOptions.SingleConnection |
|
||||
DynamicDatabaseOptions.SingleTransaction |
|
||||
DynamicDatabaseOptions.SupportLimitOffset);
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
DestroyTestDatabase();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression equal.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereEq()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Eq(0));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression equal with brackets.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereBracketsEq()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Eq(0).SetBeginBlock())
|
||||
.Where(new DynamicColumn("u.IsActive").Eq(1).SetEndBlock());
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE ((u.\"Deleted\" = [${0}]) AND (u.\"IsActive\" = [${1}]))", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression equal with brackets and or condition.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereBracketsOrEq()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Eq(0).SetBeginBlock())
|
||||
.Where(new DynamicColumn("u.IsActive").Eq(1).SetOr().SetEndBlock());
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE ((u.\"Deleted\" = [${0}]) OR (u.\"IsActive\" = [${1}]))", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression equal with brackets.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereBracketsOrEq2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Id_User").Greater(1))
|
||||
.Where(new DynamicColumn("u.Deleted").Eq(0).SetBeginBlock())
|
||||
.Where(new DynamicColumn("u.IsActive").Eq(1).SetOr().SetEndBlock());
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Id_User\" > [${0}]) AND ((u.\"Deleted\" = [${1}]) OR (u.\"IsActive\" = [${2}]))",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression equal with brackets.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereBracketsOrEqForgotToEnd()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Id_User").Greater(1))
|
||||
.Where(new DynamicColumn("u.Deleted").Eq(0).SetBeginBlock())
|
||||
.Where(new DynamicColumn("u.IsActive").Eq(1).SetOr());
|
||||
|
||||
using (var con = Database.Open())
|
||||
using (var c = con.CreateCommand())
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Id_User\" > @0) AND ((u.\"Deleted\" = @1) OR (u.\"IsActive\" = @2))"),
|
||||
c.SetCommand(cmd).CommandText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression not equal.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNotEq()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Not(0));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" <> [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression greater.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereGreater()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Greater(0));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" > [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression greater or equal.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereGreaterOrEqual()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").GreaterOrEqual(0));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" >= [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression less.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereLess()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Less(1));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" < [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression less or equal.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereLessOrEqual()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").LessOrEqual(1));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" <= [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression like.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereLike()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Like("%1"));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE u.\"Deleted\" LIKE [${0}]", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression not like.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNotLike()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").NotLike("%1"));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE u.\"Deleted\" NOT LIKE [${0}]", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression between.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereBetween()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").Between(0, 1));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE u.\"Deleted\" BETWEEN [${0}] AND [${1}]", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression in.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereIn()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new DynamicColumn("u.Deleted").In(0, 1));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE u.\"Deleted\" IN([${0}], [${1}])", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the where expression using anonymous types.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereAnon()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.Where(new { Deleted = 0, IsActive = 1, _table = "u" });
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Deleted\" = [${0}]) AND (u.\"IsActive\" = [${1}])", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the order by column.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestOrderByCol()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.OrderByColumn(new DynamicColumn("u.Name").Desc());
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u ORDER BY u.\"Name\" DESC"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the order by column number.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestOrderByNum()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.OrderByColumn(new DynamicColumn("u.Name").SetAlias("1").Desc());
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u ORDER BY 1 DESC"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the group by column.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestGroupByCol()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(x => x.dbo.Users.As(x.u))
|
||||
.GroupByColumn(new DynamicColumn("u.Name"));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u GROUP BY u.\"Name\""), cmd.CommandText());
|
||||
}
|
||||
}
|
||||
}
|
||||
997
DynamORM.Tests/Select/ParserTests.cs
Normal file
997
DynamORM.Tests/Select/ParserTests.cs
Normal file
@@ -0,0 +1,997 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>
|
||||
/// New parser tests.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class ParserTests : TestsBase
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
CreateDynamicDatabase(
|
||||
DynamicDatabaseOptions.SingleConnection |
|
||||
DynamicDatabaseOptions.SingleTransaction |
|
||||
DynamicDatabaseOptions.SupportLimitOffset |
|
||||
DynamicDatabaseOptions.SupportNoLock);
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
try
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
DestroyTestDatabase();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromGet()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users);
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method with multi tables.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromGetMultiKulti()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users, c => c.Clients);
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\", \"Clients\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method with as expression in text.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromGetAs1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As("c"));
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method with as expression in text.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromGetAsNoLock1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As("c").NoLock());
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c WITH(NOLOCK)", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method with as expression using lambda.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromGetAs2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c));
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using text.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromText()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => "dbo.Users");
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using text with decorators.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromDecoratedText()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => "\"dbo\".\"Users\"");
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using text with as.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromTextAs1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => "dbo.Users AS c");
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with as.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromTextAs2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u(u.dbo.Users).As(u.u));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with as.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromTextAs3()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u("\"dbo\".\"Users\"").As(u.u));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromSubQuery1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u(new DynamicSelectQueryBuilder(Database).From(x => x.dbo.Users)).As("u"));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM (SELECT * FROM \"dbo\".\"Users\") AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromSubQuery2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u(cmd.SubQuery(x => x.dbo.Users)).As("u"));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM (SELECT * FROM \"dbo\".\"Users\") AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromSubQuery3()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.SubQuery((b, s) => b.From(y => y(s.From(x => x.dbo.Users)).As("u")));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM (SELECT * FROM \"dbo\".\"Users\") AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestFromSubQuery4()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u(new DynamicSelectQueryBuilder(Database).From(x => x.dbo.Users.NoLock())).As("u"));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM (SELECT * FROM \"dbo\".\"Users\" WITH(NOLOCK)) AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereAlias()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.UserName == "admin");
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE (c.\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestHavingAlias()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Having(u => u.Sum(u.c.ClientsCount) > 10);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c HAVING (Sum(c.\"ClientsCount\") > [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests complex where method with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereAliasComplex()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.UserName == "admin" || u.c.UserName == "root")
|
||||
.Where(u => u.c.IsActive = true);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND c.\"IsActive\" = ([${2}])",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with alias using in.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereAliasIn()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
int[] ids = new int[] { 0, 1, 2, 3, 4, 5 };
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.UserName == "admin" || u.c.UserName == "root")
|
||||
.Where(u => u.c.IsActive == true)
|
||||
.Where(u => u.c.Id_User.In(ids));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND (c.\"IsActive\" = [${2}]) AND c.\"Id_User\" IN({3})",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2],
|
||||
string.Join(", ", cmd.Parameters.Keys.Skip(3).Select(p => string.Format("[${0}]", p)))), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with alias using between.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereAliasBetween1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
int[] ids = new int[] { 0, 5 };
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.UserName == "admin" || u.c.UserName == "root")
|
||||
.Where(u => u.c.IsActive == true)
|
||||
.Where(u => u.c.Id_User.Between(ids));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND (c.\"IsActive\" = [${2}]) AND c.\"Id_User\" BETWEEN [${3}] AND [${4}]",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2],
|
||||
cmd.Parameters.Keys.ToArray()[3], cmd.Parameters.Keys.ToArray()[4]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with alias using between.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereAliasBetween2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
int[] ids = new int[] { 0, 5 };
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.UserName == "admin" || u.c.UserName == "root")
|
||||
.Where(u => u.c.IsActive == true)
|
||||
.Where(u => u.c.Id_User.Between(ids[0], ids[1]));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND (c.\"IsActive\" = [${2}]) AND c.\"Id_User\" BETWEEN [${3}] AND [${4}]",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2],
|
||||
cmd.Parameters.Keys.ToArray()[3], cmd.Parameters.Keys.ToArray()[4]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method without alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNoAlias()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users)
|
||||
.Where(u => u.UserName == "admin");
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" WHERE (\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with full column name.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestWhereNoAliasTableName()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users)
|
||||
.Where(u => u.dbo.Users.UserName == "admin");
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" WHERE (\"dbo\".\"Users\".\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests simple join method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestJoinClassic()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests inner join method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInnerJoin()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.Inner().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr INNER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests inner join method with aliases mix.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInnerJoin2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(usr => usr.Inner().dbo.UserClients.AS(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null))
|
||||
.Select(usr => usr.All(), uc => uc.Users);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr INNER JOIN \"dbo\".\"UserClients\" AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInnerJoin3()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.SubQuery((b, s) => b.Join(usr => usr(s.From(x => x.dbo.UserClients)).Inner().As(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null)))
|
||||
.Select(usr => usr.All(), uc => uc.Users);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr INNER JOIN (SELECT * FROM \"dbo\".\"UserClients\") AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInnerJoin4()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.SubQuery((b, s) => b.Join(usr => usr(s.From(x => x.dbo.UserClients).Where(x => x.Deleted == 0)).Inner().As(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null)))
|
||||
.Select(usr => usr.All(), uc => uc.Users);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr INNER JOIN (SELECT * FROM \"dbo\".\"UserClients\" WHERE (\"Deleted\" = [${0}])) AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests from method using invoke with sub query an no lock.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInnerJoin5()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr).NoLock())
|
||||
.SubQuery((b, s) => b.Join(usr => usr(s.From(x => x.dbo.UserClients.NoLock()).Where(x => x.Deleted == 0)).Inner().As(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null)))
|
||||
.Select(usr => usr.All(), uc => uc.Users);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr WITH(NOLOCK) INNER JOIN (SELECT * FROM \"dbo\".\"UserClients\" WITH(NOLOCK) WHERE (\"Deleted\" = [${0}])) AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests inner join method with no lock.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestInnerJoin6()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr).NoLock())
|
||||
.Join(u => u.Inner().dbo.UserClients.AS(u.uc).NoLock().On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr WITH(NOLOCK) INNER JOIN \"dbo\".\"UserClients\" AS uc WITH(NOLOCK) ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests left outer join method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestLeftOuterJoin()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.LeftOuter().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests left join method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestLeftJoin()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.Left().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests right outer join method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestRightOuterJoin()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.RightOuter().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests right join method.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestRightJoin()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.Right().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests complex join with parameters.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestJoinClassicWithParamAndWhere()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.usr))
|
||||
.Join(u => u.dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id && u.uc.Deleted == 0))
|
||||
.Where(u => u.usr.Active == true);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Deleted\" = [${0}])) WHERE (usr.\"Active\" = [${1}])",
|
||||
cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select all.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAll1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.c.All());
|
||||
|
||||
Assert.AreEqual("SELECT c.* FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select all.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAll2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users)
|
||||
.Select(u => u.dbo.Users.All());
|
||||
|
||||
Assert.AreEqual("SELECT \"dbo\".\"Users\".* FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select field.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectField1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.c.UserName);
|
||||
|
||||
Assert.AreEqual("SELECT c.\"UserName\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select field.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectField2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users)
|
||||
.Select(u => u.dbo.Users.UserName);
|
||||
|
||||
Assert.AreEqual("SELECT \"dbo\".\"Users\".\"UserName\" FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select field with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectFieldAlias1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.c.UserName.As(u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT c.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select field with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectFieldAlias2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users)
|
||||
.Select(u => u.dbo.Users.UserName.As(u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT \"dbo\".\"Users\".\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select field with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectFieldAlias3()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.u))
|
||||
.Select(u => u.UserName.As(u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT u.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select field with alias.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectFieldAlias4()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.u))
|
||||
.Select(u => u.UserName.As(u.u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT u.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS u", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select aggregate field with alias (Sum).
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAggregateField1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.Sum(u.c.UserName).As(u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT Sum(c.\"UserName\") AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select aggregate field with alias (Coalesce).
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAggregateField2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.Coalesce(u.c.UserName, u.c.FirstName + " " + u.c.LastName).As(u.Name));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT Coalesce(c.\"UserName\", ((c.\"FirstName\" + [${0}]) + c.\"LastName\")) AS \"Name\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select aggregate field with alias (Sum).
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAggregateField3()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users)
|
||||
.Select(u => u.Sum(u.dbo.Users.UserName));
|
||||
|
||||
Assert.AreEqual("SELECT Sum(\"dbo\".\"Users\".\"UserName\") FROM \"dbo\".\"Users\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select aggregate field with alias (Sum).
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAggregateField4()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.Sum(u("\"UserName\"")).As(u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT Sum(\"UserName\") AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select aggregate field with alias (Sum).
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAggregateField5()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u(u.Sum(u("\"UserName\"")), " + 1").As(u.Name));
|
||||
|
||||
Assert.AreEqual("SELECT Sum(\"UserName\") + 1 AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select from anonymous type.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectAnon()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => new
|
||||
{
|
||||
Id_User = u.c.Id_User,
|
||||
Name = u.c.UserName,
|
||||
});
|
||||
|
||||
Assert.AreEqual("SELECT c.\"Id_User\" AS \"Id_User\", c.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectCaseEscaped1()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u("CASE ", u.c.IsActive, " WHEN ", 1, " THEN ", 0, " ELSE ", 1, " END").As(u.Deleted));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT CASE c.\"IsActive\" WHEN [${0}] THEN [${1}] ELSE [${2}] END AS \"Deleted\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectCaseEscaped2()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u("CASE WHEN ", u.c.IsActive == 1, " THEN ", 0, " ELSE ", 1, " END").As(u.Deleted));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT CASE WHEN (c.\"IsActive\" = [${0}]) THEN [${1}] ELSE [${2}] END AS \"Deleted\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCoalesceEscaped()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u("COALESCE(", Database.DecorateName("ServerHash"), ", ", new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, ")").As(u.Hash));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT COALESCE(\"ServerHash\", [${0}]) AS \"Hash\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCoalesce()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.Coalesce(u.c.ServerHash, new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }).As(u.Hash));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT Coalesce(c.\"ServerHash\", [${0}]) AS \"Hash\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCoalesceCalculatedArgs()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.Coalesce(u.c.Test1 + "_", u.c.Test2 + "_", u.c.Test3 + "_").As(u.Hash));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT Coalesce((c.\"Test1\" + [${0}]), (c.\"Test2\" + [${1}]), (c.\"Test3\" + [${2}])) AS \"Hash\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
|
||||
//var c = Database.Open().CreateCommand();
|
||||
//cmd.FillCommand(c);
|
||||
//c.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCoalesceInWhere()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.ServerHash.As(u.Hash))
|
||||
.Where(u => u.Coalesce(u.c.ServerHash, new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) == new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT \"ServerHash\" AS \"Hash\" FROM \"dbo\".\"Users\" AS c WHERE (Coalesce(c.\"ServerHash\", [${0}]) = [${1}])",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case with sub query.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSelectCaseEscapedAndSub()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u("CASE WHEN ", u.c.IsActive == 1, " AND ", u.c.IsAdmin == u(cmd.SubQuery()
|
||||
.From(x => x.dbo.AccessRights.As(x.a))
|
||||
.Where(x => x.a.User_Id == x.c.Id_User)
|
||||
.Select(x => x.a.IsAdmin)), " THEN ", 0, " ELSE ", 1, " END").As(u.Deleted));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT CASE WHEN (c.\"IsActive\" = [${0}]) AND (c.\"IsAdmin\" = (SELECT a.\"IsAdmin\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"User_Id\" = c.\"Id_User\"))) THEN [${1}] ELSE [${2}] END AS \"Deleted\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests group by.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestGroupBy()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.GroupBy(u => u.c.UserName);
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c GROUP BY c.\"UserName\"", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests order by.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestOrderBy()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.OrderBy(u => u.c.UserName);
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY c.\"UserName\" ASC", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests order by using string with number.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestOrderByNumberedColumnStr()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.OrderBy(u => "1 DESC");
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY 1 DESC", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests order by using member with number.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestOrderByNumberedColFn()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.OrderBy(u => u.Desc(1));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY 1 DESC", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests order by using member with field.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestOrderByAlt()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.OrderBy(u => u.Desc(u.c.UserName));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY c.\"UserName\" DESC", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests sub query select.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSubQuerySelect()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u(cmd.SubQuery()
|
||||
.From(x => x.dbo.AccessRights.As(x.a))
|
||||
.Where(x => x.a.User_Id == x.c.Id_User)
|
||||
.Select(x => x.a.IsAdmin)).As(u.IsAdmin));
|
||||
|
||||
Assert.AreEqual("SELECT (SELECT a.\"IsAdmin\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"User_Id\" = c.\"Id_User\")) AS \"IsAdmin\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests sub query where.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSubQueryWhere()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.IsAdmin == u(cmd.SubQuery()
|
||||
.From(x => x.dbo.AccessRights.As(x.a))
|
||||
.Where(x => x.a.User_Id == x.c.Id_User)
|
||||
.Select(x => x.a.IsAdmin)));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c WHERE (c.\"IsAdmin\" = (SELECT a.\"IsAdmin\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"User_Id\" = c.\"Id_User\")))", cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests sub query in.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSubQueryWhereIn()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Where(u => u.c.Id_User.In(u(cmd.SubQuery()
|
||||
.From(x => x.dbo.AccessRights.As(x.a))
|
||||
.Where(x => x.a.IsAdmin == 1)
|
||||
.Select(x => x.a.User_Id))));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE c.\"Id_User\" IN((SELECT a.\"User_Id\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"IsAdmin\" = [${0}])))", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests sub query join.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSubQueryJoin()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Join(u => u.Inner()(cmd.SubQuery()
|
||||
.From(x => x.dbo.AccessRights.As(x.a))
|
||||
.Select(x => x.a.IsAdmin, x => x.a.User_Id)).As(u.ar).On(u.ar.User_Id == u.c.Id_User));
|
||||
|
||||
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c INNER JOIN (SELECT a.\"IsAdmin\", a.\"User_Id\" FROM \"dbo\".\"AccessRights\" AS a) AS ar ON (ar.\"User_Id\" = c.\"Id_User\")", cmd.CommandText());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -34,6 +34,7 @@ using NUnit.Framework;
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>Test typed ORM.</summary>
|
||||
[TestFixture]
|
||||
public class RenamedTypedAccessTests : TypedAccessTests<Users>
|
||||
{
|
||||
/// <summary>Test something fancy... like: <code>select "first", count("first") aggregatefield from "users" group by "first" order by 2 desc;</code>.</summary>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -29,29 +29,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Tests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
/// <summary>Test typed ORM.</summary>
|
||||
/// <typeparam name="T">Type to test.</typeparam>
|
||||
[TestFixture(typeof(users))]
|
||||
public class TypedAccessTests<T> : TestsBase
|
||||
[TestClass]
|
||||
public class TypedAccessTests<T> : TestsBase where T : class
|
||||
{
|
||||
/// <summary>Setup test parameters.</summary>
|
||||
[TestFixtureSetUp]
|
||||
[TestInitialize]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
CreateTestDatabase();
|
||||
CreateDynamicDatabase();
|
||||
|
||||
// Cache table (profiler freaks out)
|
||||
GetTestTable();
|
||||
}
|
||||
|
||||
/// <summary>Tear down test objects.</summary>
|
||||
[TestFixtureTearDown]
|
||||
[TestCleanup]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
DestroyDynamicDatabase();
|
||||
@@ -65,10 +63,17 @@ namespace DynamORM.Tests.Select
|
||||
return Database.Table();
|
||||
}
|
||||
|
||||
/// <summary>Create table using specified method.</summary>
|
||||
/// <returns>Dynamic table.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder GetTestBuilder()
|
||||
{
|
||||
return Database.Table().Query() as IDynamicSelectQueryBuilder;
|
||||
}
|
||||
|
||||
#region Select typed
|
||||
|
||||
/// <summary>Test load all rows into mapped list alternate way.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedGetAll()
|
||||
{
|
||||
var list = (GetTestTable().Query(type: typeof(T)) as IEnumerable<object>).Cast<T>().ToList();
|
||||
@@ -76,22 +81,57 @@ namespace DynamORM.Tests.Select
|
||||
Assert.AreEqual(200, list.Count);
|
||||
}
|
||||
|
||||
/// <summary>Test load all rows into mapped list alternate way.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedGetAll2()
|
||||
{
|
||||
var list = GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Execute()
|
||||
.MapEnumerable<T>()
|
||||
.ToList();
|
||||
|
||||
Assert.AreEqual(200, list.Count);
|
||||
}
|
||||
|
||||
/// <summary>Test load all rows into mapped list alternate way.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedGetAll3()
|
||||
{
|
||||
var list = GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Execute<T>()
|
||||
.ToList();
|
||||
|
||||
Assert.AreEqual(200, list.Count);
|
||||
}
|
||||
|
||||
/// <summary>Test unknown op.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedUnknownOperation()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => GetTestTable().MakeMeASandwitch(type: typeof(T), with: "cheese"));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => GetTestTable().MakeMeASandwitch(type: typeof(T), with: "cheese"));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Count</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedCount()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Count(type: typeof(T), columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in steatement.</summary>
|
||||
[Test]
|
||||
/// <summary>Test typed <c>Count</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedCount2()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.Count(x.t.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSelectInEnumerableCount()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestTable().Count(type: typeof(T), last: new DynamicColumn
|
||||
@@ -101,8 +141,19 @@ namespace DynamORM.Tests.Select
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in steatement.</summary>
|
||||
[Test]
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSelectInEnumerableCount2()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.last.In(new object[] { "Hendricks", "Goodwin", "Freeman" }.Take(3)))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSelectInArrayCount()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestTable().Count(type: typeof(T), last: new DynamicColumn
|
||||
@@ -112,71 +163,155 @@ namespace DynamORM.Tests.Select
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSelectInArrayCount2()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.last.In(new object[] { "Hendricks", "Goodwin", "Freeman" }))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>First</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedFirst()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().First(type: typeof(T), columns: "id").id);
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>First</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedFirst2()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.t.id)
|
||||
.Execute()
|
||||
.First().id);
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Last</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedLast()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Last(type: typeof(T), columns: "id").id);
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Last</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedLast2()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.t.id)
|
||||
.Execute()
|
||||
.Last().id);
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Count</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedCountSpecificRecord()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Count(type: typeof(T), first: "Ori"));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedMin()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Min(type: typeof(T), columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedMin2()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.Min(x.t.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Min</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedMax()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Max(type: typeof(T), columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedMax2()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.Max(x.t.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Min</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedtAvg()
|
||||
{
|
||||
Assert.AreEqual(100.5, GetTestTable().Avg(type: typeof(T), columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Min</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedtAvg2()
|
||||
{
|
||||
Assert.AreEqual(100.5, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.Avg(x.t.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Sum</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSum()
|
||||
{
|
||||
Assert.AreEqual(20100, GetTestTable().Sum(type: typeof(T), columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Sum</c> method.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSum2()
|
||||
{
|
||||
Assert.AreEqual(20100, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.Sum(x.t.id))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method for invalid operation exception.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedScalarException()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => GetTestTable().Scalar(type: typeof(T), id: 19));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => GetTestTable().Scalar(type: typeof(T), id: 19));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedScalar()
|
||||
{
|
||||
Assert.AreEqual("Ori", GetTestTable().Scalar(type: typeof(T), columns: "first", id: 19));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method.</summary>
|
||||
[TestMethod]
|
||||
public void TestTypedScalar2()
|
||||
{
|
||||
Assert.AreEqual("Ori", GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id == 19)
|
||||
.Select(x => x.t.first)
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method with SQLite specific aggregate.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedScalarGroupConcat()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
@@ -185,8 +320,22 @@ namespace DynamORM.Tests.Select
|
||||
GetTestTable().Scalar(type: typeof(T), columns: "first:first:group_concat", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 }));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method with SQLite specific aggregate.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedScalarGroupConcat2()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
// select group_concat("first") AS first from "users" where "id" < 20;
|
||||
Assert.AreEqual("Clarke,Marny,Dai,Forrest,Blossom,George,Ivory,Inez,Sigourney,Fulton,Logan,Anne,Alexandra,Adena,Lionel,Aimee,Selma,Lara,Ori",
|
||||
GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id < 20)
|
||||
.Select(x => x.group_concat(x.t.first).As(x.first))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method with SQLite specific aggregate not using aggregate field.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedScalarGroupConcatNoAggregateField()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
@@ -195,8 +344,22 @@ namespace DynamORM.Tests.Select
|
||||
GetTestTable().Scalar(type: typeof(T), columns: "group_concat(first):first", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 }));
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Scalar</c> method with SQLite specific aggregate not using aggregate field.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedScalarGroupConcatNoAggregateField2()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
// select group_concat("first") AS first from "users" where "id" < 20;
|
||||
Assert.AreEqual("Clarke,Marny,Dai,Forrest,Blossom,George,Ivory,Inez,Sigourney,Fulton,Logan,Anne,Alexandra,Adena,Lionel,Aimee,Selma,Lara,Ori",
|
||||
GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id < 20)
|
||||
.SelectColumn("group_concat(first):first")
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test something fancy... like: <code>select "first", count("first") aggregatefield from "users" group by "first" order by 2 desc;</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedFancyAggregateQuery()
|
||||
{
|
||||
var v = (GetTestTable().Query(type: typeof(T), columns: "first,first:aggregatefield:count", group: "first", order: ":desc:2") as IEnumerable<dynamic>).ToList();
|
||||
@@ -210,20 +373,61 @@ namespace DynamORM.Tests.Select
|
||||
Assert.AreEqual(1, v.Last().aggregatefield);
|
||||
}
|
||||
|
||||
/// <summary>Test something fancy... like: <code>select "first", count("first") aggregatefield from "users" group by "first" order by 2 desc;</code>.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedFancyAggregateQuery2()
|
||||
{
|
||||
var v = GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.t.first, x => x.Count(x.t.first).As(x.aggregatefield))
|
||||
.GroupBy(x => x.t.first)
|
||||
.OrderBy(x => x.Desc(2))
|
||||
.Execute()
|
||||
.ToList();
|
||||
|
||||
Assert.IsNotNull(v);
|
||||
Assert.AreEqual(187, v.Count());
|
||||
Assert.AreEqual(4, v.First().aggregatefield);
|
||||
Assert.AreEqual("Logan", v.First().first);
|
||||
Assert.AreEqual(2, v.Take(10).Last().aggregatefield);
|
||||
Assert.AreEqual(1, v.Take(11).Last().aggregatefield);
|
||||
Assert.AreEqual(1, v.Last().aggregatefield);
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("login")) len from "users";</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedAggregateInAggregate()
|
||||
{
|
||||
Assert.AreEqual(12.77, GetTestTable().Scalar(type: typeof(T), columns: @"length(""login""):len:avg"));
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("login")) len from "users";</code>.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedAggregateInAggregate2()
|
||||
{
|
||||
Assert.AreEqual(12.77, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => x.Avg(x.Length(x.t.login)).As(x.len))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("email")) len from "users";</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedAggregateInAggregateMark2()
|
||||
{
|
||||
Assert.AreEqual(27.7, GetTestTable().Avg(type: typeof(T), columns: @"length(""email""):len"));
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("email")) len from "users";</code>.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedAggregateInAggregateMark3()
|
||||
{
|
||||
Assert.AreEqual(27.7, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Select(x => "AVG(LENGTH(t.email)) AS LEN")
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test emails longer than 27 chars. <code>select count(*) from "users" where length("email") > 27;</code>.</summary>
|
||||
public virtual void TestTypedFunctionInWhere()
|
||||
{
|
||||
@@ -238,8 +442,18 @@ namespace DynamORM.Tests.Select
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>Test emails longer than 27 chars. <code>select count(*) from "users" where length("email") > 27;</code>.</summary>
|
||||
public virtual void TestTypedFunctionInWhere2()
|
||||
{
|
||||
Assert.AreEqual(97, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.Length(x.t.email) > 27)
|
||||
.Select(x => x.Count(x.t.All()))
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Single</c> multi.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSingleObject()
|
||||
{
|
||||
var exp = new { id = 19, first = "Ori", last = "Ellis" };
|
||||
@@ -250,93 +464,238 @@ namespace DynamORM.Tests.Select
|
||||
Assert.AreEqual(exp.last, o.last);
|
||||
}
|
||||
|
||||
/// <summary>Test typed <c>Single</c> multi.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedSingleObject2()
|
||||
{
|
||||
var exp = new { id = 19, first = "Ori", last = "Ellis" };
|
||||
var o = GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id == 19)
|
||||
.Select(x => new { id = x.t.id, first = x.t.first, last = x.t.last })
|
||||
.Execute()
|
||||
.First();
|
||||
|
||||
Assert.AreEqual(exp.id, o.id);
|
||||
Assert.AreEqual(exp.first, o.first);
|
||||
Assert.AreEqual(exp.last, o.last);
|
||||
}
|
||||
|
||||
#endregion Select typed
|
||||
|
||||
#region Where typed
|
||||
|
||||
/// <summary>Test typed where expression equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereEq()
|
||||
{
|
||||
Assert.AreEqual("hoyt.tran", GetTestTable().Single(type: typeof(T), where: new DynamicColumn("id").Eq(100)).login);
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression equal.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereEq2()
|
||||
{
|
||||
Assert.AreEqual("hoyt.tran", GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id == 100).Execute().First().login);
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression not equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereNot()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").Not(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression not equal.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereNot2()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id != 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression like.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereLike()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Single(type: typeof(T), where: new DynamicColumn("login").Like("Hoyt.%")).id);
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression like.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereLike2()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.login.Like("Hoyt.%")).Execute().First().id);
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression not like.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereNotLike()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("login").NotLike("Hoyt.%")));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression not like.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereNotLike2()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.login.NotLike("Hoyt.%"))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression not like.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereNotLike3()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => !x.t.login.Like("Hoyt.%"))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression greater.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereGt()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").Greater(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression greater.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereGt2()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id > 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression greater or equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereGte()
|
||||
{
|
||||
Assert.AreEqual(101, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").GreaterOrEqual(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression greater or equal.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereGte2()
|
||||
{
|
||||
Assert.AreEqual(101, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id >= 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression less.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereLt()
|
||||
{
|
||||
Assert.AreEqual(99, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").Less(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression less.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereLt2()
|
||||
{
|
||||
Assert.AreEqual(99, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id < 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression less or equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereLte()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").LessOrEqual(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression less or equal.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereLte2()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id <= 100)
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression between.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereBetween()
|
||||
{
|
||||
Assert.AreEqual(26, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").Between(75, 100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression in params.</summary>
|
||||
[Test]
|
||||
/// <summary>Test typed where expression between.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereBetween2()
|
||||
{
|
||||
Assert.AreEqual(26, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id.Between(75, 100))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression in parameters.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereIn1()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").In(75, 99, 100)));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression in array.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereIn2()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestTable().Count(type: typeof(T), where: new DynamicColumn("id").In(new[] { 75, 99, 100 })));
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression in parameters.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereIn3()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id.In(75, 99, 100))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
/// <summary>Test typed where expression in array.</summary>
|
||||
[TestMethod]
|
||||
public virtual void TestTypedWhereIn4()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestBuilder()
|
||||
.From(x => x(typeof(T)).As(x.t))
|
||||
.Where(x => x.t.id.In(new[] { 75, 99, 100 }))
|
||||
.Select(x => x.Count())
|
||||
.Scalar());
|
||||
}
|
||||
|
||||
#endregion Where typed
|
||||
|
||||
#region Select generic
|
||||
|
||||
/// <summary>Test load all rows into mapped list alternate way.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericGetAll()
|
||||
{
|
||||
var list = (GetTestTable().Query<T>() as IEnumerable<object>).Cast<T>().ToList();
|
||||
@@ -345,21 +704,21 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Test unknown op.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericUnknownOperation()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => GetTestTable().MakeMeASandwitch<T>(with: "cheese"));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => GetTestTable().MakeMeASandwitch<T>(with: "cheese"));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Count</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericCount()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Count<T>(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericSelectInEnumerableCount()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestTable().Count<T>(last: new DynamicColumn
|
||||
@@ -370,7 +729,7 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Test count with in statement.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericSelectInArrayCount()
|
||||
{
|
||||
Assert.AreEqual(4, GetTestTable().Count<T>(last: new DynamicColumn
|
||||
@@ -381,70 +740,70 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>First</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericFirst()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().First<T>(columns: "id").id);
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Last</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericLast()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Last<T>(columns: "id").id);
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Count</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericCountSpecificRecord()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Count<T>(first: "Ori"));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericMin()
|
||||
{
|
||||
Assert.AreEqual(1, GetTestTable().Min<T>(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericMax()
|
||||
{
|
||||
Assert.AreEqual(200, GetTestTable().Max<T>(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Min</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenerictAvg()
|
||||
{
|
||||
Assert.AreEqual(100.5, GetTestTable().Avg<T>(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Sum</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericSum()
|
||||
{
|
||||
Assert.AreEqual(20100, GetTestTable().Sum<T>(columns: "id"));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Scalar</c> method for invalid operation exception.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericScalarException()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => GetTestTable().Scalar<T>(id: 19));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => GetTestTable().Scalar<T>(id: 19));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Scalar</c> method.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericScalar()
|
||||
{
|
||||
Assert.AreEqual("Ori", GetTestTable().Scalar<T>(columns: "first", id: 19));
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Scalar</c> method with SQLite specific aggregate.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericScalarGroupConcat()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
@@ -454,7 +813,7 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Scalar</c> method with SQLite specific aggregate not using aggregate field.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericScalarGroupConcatNoAggregateField()
|
||||
{
|
||||
// This test should produce something like this:
|
||||
@@ -464,7 +823,7 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Test something fancy... like: <code>select "first", count("first") aggregatefield from "users" group by "first" order by 2 desc;</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericFancyAggregateQuery()
|
||||
{
|
||||
var v = (GetTestTable().Query<T>(columns: "first,first:aggregatefield:count", group: "first", order: ":desc:2") as IEnumerable<dynamic>).ToList();
|
||||
@@ -479,14 +838,14 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("login")) len from "users";</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericAggregateInAggregate()
|
||||
{
|
||||
Assert.AreEqual(12.77, GetTestTable().Scalar<T>(columns: @"length(""login""):len:avg"));
|
||||
}
|
||||
|
||||
/// <summary>This time also something fancy... aggregate in aggregate <code>select AVG(LENGTH("email")) len from "users";</code>.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericAggregateInAggregateMark2()
|
||||
{
|
||||
Assert.AreEqual(27.7, GetTestTable().Avg<T>(columns: @"length(""email""):len"));
|
||||
@@ -507,7 +866,7 @@ namespace DynamORM.Tests.Select
|
||||
}
|
||||
|
||||
/// <summary>Test generic <c>Single</c> multi.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericSingleObject()
|
||||
{
|
||||
var exp = new { id = 19, first = "Ori", last = "Ellis" };
|
||||
@@ -523,77 +882,77 @@ namespace DynamORM.Tests.Select
|
||||
#region Where generic
|
||||
|
||||
/// <summary>Test generic where expression equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereEq()
|
||||
{
|
||||
Assert.AreEqual("hoyt.tran", GetTestTable().Single<T>(where: new DynamicColumn("id").Eq(100)).login);
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression not equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereNot()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestTable().Count<T>(where: new DynamicColumn("id").Not(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression like.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereLike()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Single<T>(where: new DynamicColumn("login").Like("Hoyt.%")).id);
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression not like.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereNotLike()
|
||||
{
|
||||
Assert.AreEqual(199, GetTestTable().Count<T>(where: new DynamicColumn("login").NotLike("Hoyt.%")));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression greater.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereGt()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Count<T>(where: new DynamicColumn("id").Greater(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression greater or equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereGte()
|
||||
{
|
||||
Assert.AreEqual(101, GetTestTable().Count<T>(where: new DynamicColumn("id").GreaterOrEqual(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression less.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereLt()
|
||||
{
|
||||
Assert.AreEqual(99, GetTestTable().Count<T>(where: new DynamicColumn("id").Less(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression less or equal.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereLte()
|
||||
{
|
||||
Assert.AreEqual(100, GetTestTable().Count<T>(where: new DynamicColumn("id").LessOrEqual(100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression between.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereBetween()
|
||||
{
|
||||
Assert.AreEqual(26, GetTestTable().Count<T>(where: new DynamicColumn("id").Between(75, 100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression in parameters.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereIn1()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestTable().Count<T>(where: new DynamicColumn("id").In(75, 99, 100)));
|
||||
}
|
||||
|
||||
/// <summary>Test generic where expression in array.</summary>
|
||||
[Test]
|
||||
[TestMethod]
|
||||
public virtual void TestGenericWhereIn2()
|
||||
{
|
||||
Assert.AreEqual(3, GetTestTable().Count<T>(where: new DynamicColumn("id").In(new[] { 75, 99, 100 })));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
|
||||
Binary file not shown.
Binary file not shown.
196
DynamORM.Tests/obj/DynamORM.Tests.csproj.nuget.dgspec.json
Normal file
196
DynamORM.Tests/obj/DynamORM.Tests.csproj.nuget.dgspec.json
Normal file
@@ -0,0 +1,196 @@
|
||||
{
|
||||
"format": 1,
|
||||
"restore": {
|
||||
"D:\\Source\\.NET\\DynamORM\\DynamORM.Tests\\DynamORM.Tests.csproj": {}
|
||||
},
|
||||
"projects": {
|
||||
"D:\\Source\\.NET\\DynamORM\\DynamORM.Tests\\DynamORM.Tests.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "D:\\Source\\.NET\\DynamORM\\DynamORM.Tests\\DynamORM.Tests.csproj",
|
||||
"projectName": "DynamORM.Tests",
|
||||
"projectPath": "D:\\Source\\.NET\\DynamORM\\DynamORM.Tests\\DynamORM.Tests.csproj",
|
||||
"packagesPath": "C:\\Users\\gruss\\.nuget\\packages\\",
|
||||
"outputPath": "D:\\Source\\.NET\\DynamORM\\DynamORM.Tests\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"skipContentFileWrite": true,
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\gruss\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net48"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net48": {
|
||||
"projectReferences": {
|
||||
"D:\\Source\\.NET\\DynamORM\\DynamORM\\DynamORM.csproj": {
|
||||
"projectPath": "D:\\Source\\.NET\\DynamORM\\DynamORM\\DynamORM.csproj"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"net48": {
|
||||
"dependencies": {
|
||||
"MSTest.TestAdapter": {
|
||||
"target": "Package",
|
||||
"version": "[2.0.0-beta4, )"
|
||||
},
|
||||
"MSTest.TestFramework": {
|
||||
"target": "Package",
|
||||
"version": "[2.0.0-beta4, )"
|
||||
},
|
||||
"System.Data.SQLite": {
|
||||
"target": "Package",
|
||||
"version": "[1.0.111, )"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"runtimes": {
|
||||
"win": {
|
||||
"#import": []
|
||||
},
|
||||
"win-x64": {
|
||||
"#import": []
|
||||
},
|
||||
"win-x86": {
|
||||
"#import": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"D:\\Source\\.NET\\DynamORM\\DynamORM\\DynamORM.csproj": {
|
||||
"version": "1.3.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "D:\\Source\\.NET\\DynamORM\\DynamORM\\DynamORM.csproj",
|
||||
"projectName": "DynamORM",
|
||||
"projectPath": "D:\\Source\\.NET\\DynamORM\\DynamORM\\DynamORM.csproj",
|
||||
"packagesPath": "C:\\Users\\gruss\\.nuget\\packages\\",
|
||||
"outputPath": "D:\\Source\\.NET\\DynamORM\\DynamORM\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"crossTargeting": true,
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\gruss\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net472",
|
||||
"net6.0",
|
||||
"netstandard2.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net6.0": {
|
||||
"targetAlias": "net6.0",
|
||||
"projectReferences": {}
|
||||
},
|
||||
"net472": {
|
||||
"targetAlias": "net472",
|
||||
"projectReferences": {}
|
||||
},
|
||||
"netstandard2.0": {
|
||||
"targetAlias": "netstandard2.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"net6.0": {
|
||||
"targetAlias": "net6.0",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": {
|
||||
"target": "Package",
|
||||
"version": "[4.7.0, )"
|
||||
},
|
||||
"System.Data.Common": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\6.0.201\\RuntimeIdentifierGraph.json"
|
||||
},
|
||||
"net472": {
|
||||
"targetAlias": "net472",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": {
|
||||
"target": "Package",
|
||||
"version": "[4.7.0, )"
|
||||
},
|
||||
"System.Data.Common": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.0, )"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\6.0.201\\RuntimeIdentifierGraph.json"
|
||||
},
|
||||
"netstandard2.0": {
|
||||
"targetAlias": "netstandard2.0",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": {
|
||||
"target": "Package",
|
||||
"version": "[4.7.0, )"
|
||||
},
|
||||
"NETStandard.Library": {
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[2.0.3, )",
|
||||
"autoReferenced": true
|
||||
},
|
||||
"System.Data.Common": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\6.0.201\\RuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
DynamORM.Tests/obj/DynamORM.Tests.csproj.nuget.g.props
Normal file
23
DynamORM.Tests/obj/DynamORM.Tests.csproj.nuget.g.props
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\gruss\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.1.0</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\gruss\.nuget\packages\" />
|
||||
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||
</ItemGroup>
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)mstest.testadapter\2.0.0-beta4\build\net45\MSTest.TestAdapter.props" Condition="Exists('$(NuGetPackageRoot)mstest.testadapter\2.0.0-beta4\build\net45\MSTest.TestAdapter.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgEntityFramework Condition=" '$(PkgEntityFramework)' == '' ">C:\Users\gruss\.nuget\packages\entityframework\6.2.0</PkgEntityFramework>
|
||||
<PkgSystem_Data_SQLite_EF6 Condition=" '$(PkgSystem_Data_SQLite_EF6)' == '' ">C:\Users\gruss\.nuget\packages\system.data.sqlite.ef6\1.0.111</PkgSystem_Data_SQLite_EF6>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
7
DynamORM.Tests/obj/DynamORM.Tests.csproj.nuget.g.targets
Normal file
7
DynamORM.Tests/obj/DynamORM.Tests.csproj.nuget.g.targets
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)system.data.sqlite.core\1.0.111\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('$(NuGetPackageRoot)system.data.sqlite.core\1.0.111\build\net46\System.Data.SQLite.Core.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)mstest.testadapter\2.0.0-beta4\build\net45\MSTest.TestAdapter.targets" Condition="Exists('$(NuGetPackageRoot)mstest.testadapter\2.0.0-beta4\build\net45\MSTest.TestAdapter.targets')" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
1002
DynamORM.Tests/obj/project.assets.json
Normal file
1002
DynamORM.Tests/obj/project.assets.json
Normal file
File diff suppressed because it is too large
Load Diff
18
DynamORM.Tests/obj/project.nuget.cache
Normal file
18
DynamORM.Tests/obj/project.nuget.cache
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "rAFgnQ0ruwj6gTzn4dYezodFcNf5+kCOxLq8HTYaz7PuF0I0Uc4bTWz4d/BjGTmeTByLE/ZMDAWkH2yJJcIImQ==",
|
||||
"success": true,
|
||||
"projectFilePath": "D:\\Source\\.NET\\DynamORM\\DynamORM.Tests\\DynamORM.Tests.csproj",
|
||||
"expectedPackageFiles": [
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\entityframework\\6.2.0\\entityframework.6.2.0.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\microsoft.csharp\\4.7.0\\microsoft.csharp.4.7.0.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\mstest.testadapter\\2.0.0-beta4\\mstest.testadapter.2.0.0-beta4.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\mstest.testframework\\2.0.0-beta4\\mstest.testframework.2.0.0-beta4.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\system.data.common\\4.3.0\\system.data.common.4.3.0.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\system.data.sqlite\\1.0.111\\system.data.sqlite.1.0.111.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\system.data.sqlite.core\\1.0.111\\system.data.sqlite.core.1.0.111.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\system.data.sqlite.ef6\\1.0.111\\system.data.sqlite.ef6.1.0.111.nupkg.sha512",
|
||||
"C:\\Users\\gruss\\.nuget\\packages\\system.data.sqlite.linq\\1.0.111\\system.data.sqlite.linq.1.0.111.nupkg.sha512"
|
||||
],
|
||||
"logs": []
|
||||
}
|
||||
58
DynamORM.sln
58
DynamORM.sln
@@ -1,27 +1,77 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
# SharpDevelop 4.2.0.8774-RC
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamORM", "DynamORM\DynamORM.csproj", "{63963ED7-9C78-4672-A4D4-339B6E825503}"
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamORM", "DynamORM\DynamORM.csproj", "{63963ED7-9C78-4672-A4D4-339B6E825503}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamORM.Tests", "DynamORM.Tests\DynamORM.Tests.csproj", "{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmalgamationTool", "AmalgamationTool\AmalgamationTool.csproj", "{A64D2052-D0CD-488E-BF05-E5952615D926}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tester", "Tester\Tester.csproj", "{F747AA57-BEA7-4FB8-B371-546296789AEF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|Mixed Platforms = Release|Mixed Platforms
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{63963ED7-9C78-4672-A4D4-339B6E825503}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Any CPU.Build.0 = Debug|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Mixed Platforms.Build.0 = Debug|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|x86.Build.0 = Debug|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Any CPU.Build.0 = Release|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Mixed Platforms.ActiveCfg = Release|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Mixed Platforms.Build.0 = Release|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|x86.ActiveCfg = Release|x86
|
||||
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|x86.Build.0 = Release|x86
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Debug|x86.Build.0 = Debug|x86
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Release|x86.ActiveCfg = Release|x86
|
||||
{F747AA57-BEA7-4FB8-B371-546296789AEF}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {22781EB3-2148-4CA4-845A-B55265A7B5C2}
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
StartupItem = Tester\Tester.csproj
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Insert query builder.</summary>
|
||||
public class DynamicInsertQueryBuilder : DynamicQueryBuilder<DynamicInsertQueryBuilder>
|
||||
{
|
||||
/// <summary>Gets list of columns that will be selected.</summary>
|
||||
public IDictionary<string, DynamicColumn> ValueColumns { get; private set; }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.</summary>
|
||||
/// <param name="table">Parent dynamic table.</param>
|
||||
public DynamicInsertQueryBuilder(DynamicTable table)
|
||||
: base(table)
|
||||
{
|
||||
ValueColumns = new Dictionary<string, DynamicColumn>();
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="column">Insert column and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicInsertQueryBuilder Insert(DynamicColumn column)
|
||||
{
|
||||
if (ValueColumns.ContainsKey(column.ColumnName.ToLower()))
|
||||
ValueColumns[column.ColumnName.ToLower()] = column;
|
||||
else
|
||||
ValueColumns.Add(column.ColumnName.ToLower(), column);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="column">Insert column.</param>
|
||||
/// <param name="value">Insert value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicInsertQueryBuilder Insert(string column, object value)
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
var v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return Insert(v);
|
||||
}
|
||||
|
||||
if (ValueColumns.ContainsKey(column.ToLower()))
|
||||
ValueColumns[column.ToLower()].Value = value;
|
||||
else
|
||||
ValueColumns.Add(column.ToLower(), new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Value = value
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicInsertQueryBuilder Insert(object o)
|
||||
{
|
||||
var dict = o.ToDictionary();
|
||||
var mapper = DynamicMapperCache.GetMapper(o.GetType());
|
||||
|
||||
if (mapper != null)
|
||||
{
|
||||
foreach (var con in dict)
|
||||
if (!mapper.Ignored.Contains(con.Key))
|
||||
Insert(mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key, con.Value);
|
||||
}
|
||||
else
|
||||
foreach (var con in dict)
|
||||
Insert(con.Key, con.Value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public override DynamicInsertQueryBuilder Where(DynamicColumn column)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public override DynamicInsertQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public override DynamicInsertQueryBuilder Where(string column, object value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public override DynamicInsertQueryBuilder Where(object conditions, bool schema = false)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>Fill command with query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
public override IDbCommand FillCommand(IDbCommand command)
|
||||
{
|
||||
if (ValueColumns.Count == 0)
|
||||
throw new InvalidOperationException("Insert query should contain columns to change.");
|
||||
|
||||
StringBuilder builderColumns = new StringBuilder();
|
||||
StringBuilder builderValues = new StringBuilder();
|
||||
|
||||
bool first = true;
|
||||
var db = DynamicTable.Database;
|
||||
|
||||
foreach (var v in ValueColumns)
|
||||
{
|
||||
int pos = command.Parameters.Count;
|
||||
|
||||
if (!first)
|
||||
{
|
||||
builderColumns.Append(", ");
|
||||
builderValues.Append(", ");
|
||||
}
|
||||
|
||||
db.DecorateName(builderColumns, v.Value.ColumnName);
|
||||
db.GetParameterName(builderValues, pos);
|
||||
|
||||
command.AddParameter(this, v.Value);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
return command.SetCommand("INSERT INTO {0} ({1}) VALUES ({2})", db.DecorateName(TableName), builderColumns, builderValues);
|
||||
}
|
||||
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public override dynamic Execute()
|
||||
{
|
||||
return DynamicTable.Execute(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Base query builder.</summary>
|
||||
/// <typeparam name="T">Return type of methods that should return self.</typeparam>
|
||||
public abstract class DynamicQueryBuilder<T> : IDynamicQueryBuilder where T : class
|
||||
{
|
||||
/// <summary>Gets <see cref="DynamicTable"/> instance.</summary>
|
||||
public DynamicTable DynamicTable { get; private set; }
|
||||
|
||||
/// <summary>Gets where conditions.</summary>
|
||||
public List<DynamicColumn> WhereConditions { get; private set; }
|
||||
|
||||
/// <summary>Gets table schema.</summary>
|
||||
public Dictionary<string, DynamicSchemaColumn> Schema { get; private set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
||||
public bool SupportSchema { get; private set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether set parameters for null values.</summary>
|
||||
public bool VirtualMode { get; set; }
|
||||
|
||||
/// <summary>Gets table name.</summary>
|
||||
public string TableName { get; private set; }
|
||||
|
||||
/// <summary>Initializes a new instance of the DynamicQueryBuilder class.</summary>
|
||||
/// <param name="table">Parent dynamic table.</param>
|
||||
public DynamicQueryBuilder(DynamicTable table)
|
||||
{
|
||||
DynamicTable = table;
|
||||
TableName = table.TableName;
|
||||
VirtualMode = false;
|
||||
|
||||
WhereConditions = new List<DynamicColumn>();
|
||||
|
||||
SupportSchema = (DynamicTable.Database.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
|
||||
|
||||
Schema = DynamicTable.Schema;
|
||||
}
|
||||
|
||||
/// <summary>Set table name.</summary>
|
||||
/// <param name="name">Name of table.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Table(string name)
|
||||
{
|
||||
if (TableName.ToLower() != name.ToLower())
|
||||
{
|
||||
TableName = name;
|
||||
|
||||
Schema = DynamicTable.Database.GetSchema(TableName);
|
||||
|
||||
if (Schema == null)
|
||||
throw new InvalidOperationException("Can't assign type as a table for which schema can't be build.");
|
||||
}
|
||||
|
||||
return this as T;
|
||||
}
|
||||
|
||||
/// <summary>Set table name.</summary>
|
||||
/// <typeparam name="Y">Type representing table.</typeparam>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Table<Y>()
|
||||
{
|
||||
return Table(typeof(Y));
|
||||
}
|
||||
|
||||
/// <summary>Set table name.</summary>
|
||||
/// <param name="type">Type representing table.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Table(Type type)
|
||||
{
|
||||
var mapper = DynamicMapperCache.GetMapper(type);
|
||||
string name = string.Empty;
|
||||
|
||||
if (mapper == null)
|
||||
throw new InvalidOperationException("Cant assign unmapable type as a table.");
|
||||
else
|
||||
name = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||
mapper.Type.Name : mapper.Table.Name;
|
||||
|
||||
if (TableName.ToLower() != name.ToLower())
|
||||
{
|
||||
TableName = name;
|
||||
|
||||
Schema = DynamicTable.Database.GetSchema(type);
|
||||
}
|
||||
|
||||
return this as T;
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Where(DynamicColumn column)
|
||||
{
|
||||
WhereConditions.Add(column);
|
||||
|
||||
return this as T;
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Where(string column, DynamicColumn.CompareOperator op, object value)
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
var v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return Where(v);
|
||||
}
|
||||
else if (value is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (var v in (IEnumerable<DynamicColumn>)value)
|
||||
Where(v);
|
||||
|
||||
return this as T;
|
||||
}
|
||||
|
||||
WhereConditions.Add(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Operator = op,
|
||||
Value = value
|
||||
});
|
||||
|
||||
return this as T;
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Where(string column, object value)
|
||||
{
|
||||
return Where(column, DynamicColumn.CompareOperator.Eq, value);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual T Where(object conditions, bool schema = false)
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
return Where((DynamicColumn)conditions);
|
||||
|
||||
var dict = conditions.ToDictionary();
|
||||
var mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
|
||||
foreach (var con in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(con.Key))
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key : con.Key;
|
||||
|
||||
DynamicSchemaColumn? col = null;
|
||||
|
||||
if (schema)
|
||||
{
|
||||
col = Schema.TryGetNullable(colName.ToLower());
|
||||
|
||||
if (!col.HasValue || !col.Value.IsKey)
|
||||
continue;
|
||||
/*throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", condition.Key));
|
||||
|
||||
if (!col.Value.IsKey)
|
||||
continue;*/
|
||||
|
||||
colName = col.Value.Name;
|
||||
}
|
||||
|
||||
Where(colName, con.Value);
|
||||
}
|
||||
|
||||
return this as T;
|
||||
}
|
||||
|
||||
/// <summary>Get string representation of operator.</summary>
|
||||
/// <param name="op">Operator object.</param>
|
||||
/// <returns>String representation of operator.</returns>
|
||||
public string ToOperator(DynamicColumn.CompareOperator op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case DynamicColumn.CompareOperator.Eq: return "=";
|
||||
case DynamicColumn.CompareOperator.Not: return "<>";
|
||||
case DynamicColumn.CompareOperator.Like: return "LIKE";
|
||||
case DynamicColumn.CompareOperator.NotLike: return "NOT LIKE";
|
||||
case DynamicColumn.CompareOperator.Lt: return "<";
|
||||
case DynamicColumn.CompareOperator.Lte: return "<=";
|
||||
case DynamicColumn.CompareOperator.Gt: return ">";
|
||||
case DynamicColumn.CompareOperator.Gte: return ">=";
|
||||
case DynamicColumn.CompareOperator.Between:
|
||||
case DynamicColumn.CompareOperator.In:
|
||||
default:
|
||||
throw new ArgumentException(string.Format("This operator ('{0}') requires more than conversion to string.", op));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Fill where part of a query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <param name="sb">String builder.</param>
|
||||
public virtual void FillWhere(IDbCommand command, StringBuilder sb)
|
||||
{
|
||||
// Yes, this method qualifies fo refactoring... but it's fast
|
||||
bool first = true;
|
||||
var db = DynamicTable.Database;
|
||||
|
||||
foreach (var v in WhereConditions)
|
||||
{
|
||||
var col = Schema.TryGetNullable(v.ColumnName.ToLower());
|
||||
|
||||
string column = col.HasValue ? col.Value.Name : v.ColumnName;
|
||||
|
||||
if ((column.IndexOf(db.LeftDecorator) == -1 || column.IndexOf(db.LeftDecorator) == -1) &&
|
||||
(column.IndexOf('(') == -1 || column.IndexOf(')') == -1))
|
||||
column = db.DecorateName(column);
|
||||
|
||||
if ((v.Value == null || v.Value == DBNull.Value) && !VirtualMode && !v.VirtualColumn)
|
||||
{
|
||||
#region Null operators
|
||||
|
||||
if (v.Operator == DynamicColumn.CompareOperator.Not || v.Operator == DynamicColumn.CompareOperator.Eq)
|
||||
sb.AppendFormat(" {0} {1}{2} IS{3} NULL{4}",
|
||||
first ? "WHERE" : v.Or ? "OR" : "AND",
|
||||
v.BeginBlock ? "(" : string.Empty,
|
||||
column,
|
||||
v.Operator == DynamicColumn.CompareOperator.Not ? " NOT" : string.Empty,
|
||||
v.EndBlock ? ")" : string.Empty);
|
||||
else
|
||||
throw new InvalidOperationException("NULL can only be compared by IS or IS NOT operator.");
|
||||
|
||||
#endregion Null operators
|
||||
}
|
||||
else if (v.Operator != DynamicColumn.CompareOperator.In &&
|
||||
v.Operator != DynamicColumn.CompareOperator.Between)
|
||||
{
|
||||
#region Standard operators
|
||||
|
||||
int pos = command.Parameters.Count;
|
||||
|
||||
sb.AppendFormat(" {0} {1}{2} {3} ",
|
||||
first ? "WHERE" : v.Or ? "OR" : "AND",
|
||||
v.BeginBlock ? "(" : string.Empty,
|
||||
column,
|
||||
ToOperator(v.Operator));
|
||||
|
||||
db.GetParameterName(sb, pos);
|
||||
|
||||
if (v.EndBlock)
|
||||
sb.Append(")");
|
||||
|
||||
command.AddParameter(this, v);
|
||||
|
||||
#endregion Standard operators
|
||||
}
|
||||
else if (((object)v.Value).GetType().IsCollection() || v.Value is IEnumerable<object>)
|
||||
{
|
||||
#region In or Between operator
|
||||
|
||||
if (v.Operator == DynamicColumn.CompareOperator.Between)
|
||||
{
|
||||
#region Between operator
|
||||
|
||||
var vals = (v.Value as IEnumerable<object>).Take(2).ToList();
|
||||
|
||||
if (vals == null && v.Value is Array)
|
||||
vals = ((Array)v.Value).Cast<object>().ToList();
|
||||
|
||||
if (vals.Count == 2)
|
||||
{
|
||||
sb.AppendFormat(" {0} {1}{2} BETWEEN ",
|
||||
first ? "WHERE" : v.Or ? "OR" : "AND",
|
||||
v.BeginBlock ? "(" : string.Empty,
|
||||
column);
|
||||
|
||||
// From parameter
|
||||
db.GetParameterName(sb, command.Parameters.Count);
|
||||
v.Value = vals[0];
|
||||
command.AddParameter(this, v);
|
||||
|
||||
sb.Append(" AND ");
|
||||
|
||||
// To parameter
|
||||
db.GetParameterName(sb, command.Parameters.Count);
|
||||
v.Value = vals[1];
|
||||
command.AddParameter(this, v);
|
||||
|
||||
if (v.EndBlock)
|
||||
sb.Append(")");
|
||||
|
||||
// Reset value
|
||||
v.Value = vals;
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException("BETWEEN must have two values.");
|
||||
|
||||
#endregion Between operator
|
||||
}
|
||||
else if (v.Operator == DynamicColumn.CompareOperator.In)
|
||||
{
|
||||
#region In operator
|
||||
|
||||
sb.AppendFormat(" {0} {1}{2} IN(",
|
||||
first ? "WHERE" : v.Or ? "OR" : "AND",
|
||||
v.BeginBlock ? "(" : string.Empty,
|
||||
column);
|
||||
|
||||
bool firstParam = true;
|
||||
|
||||
var vals = v.Value as IEnumerable<object>;
|
||||
|
||||
if (vals == null && v.Value is Array)
|
||||
vals = ((Array)v.Value).Cast<object>() as IEnumerable<object>;
|
||||
|
||||
foreach (var val in vals)
|
||||
{
|
||||
int pos = command.Parameters.Count;
|
||||
|
||||
if (!firstParam)
|
||||
sb.Append(", ");
|
||||
|
||||
db.GetParameterName(sb, pos);
|
||||
v.Value = val;
|
||||
|
||||
command.AddParameter(this, v);
|
||||
|
||||
firstParam = false;
|
||||
}
|
||||
|
||||
v.Value = vals;
|
||||
|
||||
sb.Append(")");
|
||||
|
||||
if (v.EndBlock)
|
||||
sb.Append(")");
|
||||
|
||||
#endregion In operator
|
||||
}
|
||||
else
|
||||
throw new Exception("BAZINGA. You have reached unreachable code.");
|
||||
|
||||
#endregion In or Between operator
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException(
|
||||
string.Format("Operator was {0}, but value wasn't enumerable. Column: '{1}'", v.Operator.ToString().ToUpper(), col));
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Fill command with query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
public abstract IDbCommand FillCommand(IDbCommand command);
|
||||
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Result of an execution..</returns>
|
||||
public abstract dynamic Execute();
|
||||
}
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Select query builder.</summary>
|
||||
public class DynamicSelectQueryBuilder : DynamicQueryBuilder<DynamicSelectQueryBuilder>
|
||||
{
|
||||
/// <summary>Gets dictionary of columns that will be selected.</summary>
|
||||
public List<DynamicColumn> Columns { get; private set; }
|
||||
|
||||
/// <summary>Gets dictionary of columns that will be used to group query.</summary>
|
||||
public List<DynamicColumn> Group { get; private set; }
|
||||
|
||||
/// <summary>Gets dictionary of columns that will be used to order query.</summary>
|
||||
public List<DynamicColumn> Order { get; private set; }
|
||||
|
||||
private int? _top = null;
|
||||
private int? _limit = null;
|
||||
private int? _offset = null;
|
||||
private bool _distinct = false;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicSelectQueryBuilder" /> class.</summary>
|
||||
/// <param name="table">Parent dynamic table.</param>
|
||||
public DynamicSelectQueryBuilder(DynamicTable table)
|
||||
: base(table)
|
||||
{
|
||||
Columns = new List<DynamicColumn>();
|
||||
Group = new List<DynamicColumn>();
|
||||
Order = new List<DynamicColumn>();
|
||||
}
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to add to object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder Select(params DynamicColumn[] columns)
|
||||
{
|
||||
foreach (var col in columns)
|
||||
Columns.Add(col);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to add to object.</param>
|
||||
/// <remarks>Column format consist of <c>Column Name</c>, <c>Alias</c> and
|
||||
/// <c>Aggregate function</c> in this order separated by '<c>:</c>'.</remarks>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder Select(params string[] columns)
|
||||
{
|
||||
return Select(columns.Select(c => DynamicColumn.ParseSelectColumn(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to group by.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder GroupBy(params DynamicColumn[] columns)
|
||||
{
|
||||
foreach (var col in columns)
|
||||
Group.Add(col);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to group by.</param>
|
||||
/// <remarks>Column format consist of <c>Column Name</c> and
|
||||
/// <c>Alias</c> in this order separated by '<c>:</c>'.</remarks>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder GroupBy(params string[] columns)
|
||||
{
|
||||
return GroupBy(columns.Select(c => DynamicColumn.ParseSelectColumn(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to order by.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder OrderBy(params DynamicColumn[] columns)
|
||||
{
|
||||
foreach (var col in columns)
|
||||
Order.Add(col);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to order by.</param>
|
||||
/// <remarks>Column format consist of <c>Column Name</c> and
|
||||
/// <c>Alias</c> in this order separated by '<c>:</c>'.</remarks>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder OrderBy(params string[] columns)
|
||||
{
|
||||
return OrderBy(columns.Select(c => DynamicColumn.ParseOrderByColumn(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>Set top if database support it.</summary>
|
||||
/// <param name="top">How many objects select.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder Top(int? top)
|
||||
{
|
||||
if ((DynamicTable.Database.Options & DynamicDatabaseOptions.SupportTop) != DynamicDatabaseOptions.SupportTop)
|
||||
throw new NotSupportedException("Database doesn't support TOP clause.");
|
||||
|
||||
_top = top;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Set top if database support it.</summary>
|
||||
/// <param name="limit">How many objects select.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder Limit(int? limit)
|
||||
{
|
||||
if ((DynamicTable.Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset)
|
||||
throw new NotSupportedException("Database doesn't support LIMIT clause.");
|
||||
|
||||
_limit = limit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Set top if database support it.</summary>
|
||||
/// <param name="offset">How many objects skip selecting.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder Offset(int? offset)
|
||||
{
|
||||
if ((DynamicTable.Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset)
|
||||
throw new NotSupportedException("Database doesn't support OFFSET clause.");
|
||||
|
||||
_offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Set distinct mode.</summary>
|
||||
/// <param name="distinct">Distinct mode.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public DynamicSelectQueryBuilder Distinct(bool distinct = true)
|
||||
{
|
||||
_distinct = distinct;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Fill command with query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
public override IDbCommand FillCommand(IDbCommand command)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
var db = DynamicTable.Database;
|
||||
|
||||
sb.AppendFormat("SELECT{0}{1} ",
|
||||
_distinct ? " DISTINCT" : string.Empty,
|
||||
_top.HasValue ? string.Format(" TOP {0}", _top.Value) : string.Empty);
|
||||
|
||||
BuildColumns(sb, db);
|
||||
|
||||
sb.Append(" FROM ");
|
||||
db.DecorateName(sb, TableName);
|
||||
|
||||
FillWhere(command, sb);
|
||||
|
||||
BuildGroup(sb, db);
|
||||
BuildOrder(sb, db);
|
||||
|
||||
if (_limit.HasValue)
|
||||
sb.AppendFormat(" LIMIT {0}", _limit.Value);
|
||||
|
||||
if (_offset.HasValue)
|
||||
sb.AppendFormat(" OFFSET {0}", _offset.Value);
|
||||
|
||||
return command.SetCommand(sb.ToString());
|
||||
}
|
||||
|
||||
private void BuildColumns(StringBuilder sb, DynamicDatabase db)
|
||||
{
|
||||
if (Columns.Count > 0)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
// Not pretty but blazing fast
|
||||
Columns.ForEach(c =>
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
|
||||
c.ToSQLSelectColumn(db, sb);
|
||||
first = false;
|
||||
});
|
||||
}
|
||||
else
|
||||
sb.Append("*");
|
||||
}
|
||||
|
||||
private void BuildGroup(StringBuilder sb, DynamicDatabase db)
|
||||
{
|
||||
if (Group.Count > 0)
|
||||
{
|
||||
sb.Append(" GROUP BY ");
|
||||
bool first = true;
|
||||
|
||||
// Not pretty but blazing fast
|
||||
Group.ForEach(c =>
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
|
||||
c.ToSQLGroupByColumn(db, sb);
|
||||
first = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildOrder(StringBuilder sb, DynamicDatabase db)
|
||||
{
|
||||
if (Order.Count > 0)
|
||||
{
|
||||
sb.Append(" ORDER BY ");
|
||||
bool first = true;
|
||||
|
||||
// Not pretty but blazing fast
|
||||
Order.ForEach(c =>
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
|
||||
c.ToSQLOrderByColumn(db, sb);
|
||||
first = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Enumerator of objects expanded from query.</returns>
|
||||
public override dynamic Execute()
|
||||
{
|
||||
if (Columns.Count <= 1 && ((_top.HasValue && _top.Value == 1) || (_limit.HasValue && _limit.Value == 1)))
|
||||
return DynamicTable.Scalar(this);
|
||||
else
|
||||
return DynamicTable.Query(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Update query builder.</summary>
|
||||
public class DynamicUpdateQueryBuilder : DynamicQueryBuilder<DynamicUpdateQueryBuilder>
|
||||
{
|
||||
/// <summary>Gets list of columns that will be selected.</summary>
|
||||
public IDictionary<string, DynamicColumn> ValueColumns { get; private set; }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicUpdateQueryBuilder" /> class.</summary>
|
||||
/// <param name="table">Parent dynamic table.</param>
|
||||
public DynamicUpdateQueryBuilder(DynamicTable table)
|
||||
: base(table)
|
||||
{
|
||||
ValueColumns = new Dictionary<string, DynamicColumn>();
|
||||
}
|
||||
|
||||
/// <summary>Add update value or where condition using schema.</summary>
|
||||
/// <param name="column">Update or where column name and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicUpdateQueryBuilder Update(DynamicColumn column)
|
||||
{
|
||||
var col = Schema.TryGetNullable(column.ColumnName.ToLower());
|
||||
|
||||
if (!col.HasValue && SupportSchema)
|
||||
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
|
||||
|
||||
if (col.HasValue && col.Value.IsKey)
|
||||
Where(column);
|
||||
else
|
||||
Values(column.ColumnName, column.Value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add update value or where condition using schema.</summary>
|
||||
/// <param name="column">Update or where column name.</param>
|
||||
/// <param name="value">Column value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicUpdateQueryBuilder Update(string column, object value)
|
||||
{
|
||||
var col = Schema.TryGetNullable(column.ToLower());
|
||||
|
||||
if (!col.HasValue && SupportSchema)
|
||||
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
|
||||
|
||||
if (col.HasValue && col.Value.IsKey)
|
||||
Where(column, value);
|
||||
else
|
||||
Values(column, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add update values and where condition columns using schema.</summary>
|
||||
/// <param name="conditions">Set values or conditions as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicUpdateQueryBuilder Update(object conditions)
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
return Update((DynamicColumn)conditions);
|
||||
|
||||
var dict = conditions.ToDictionary();
|
||||
var mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
|
||||
foreach (var con in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(con.Key))
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key : con.Key;
|
||||
var col = Schema.TryGetNullable(colName.ToLower());
|
||||
|
||||
if (!col.HasValue && SupportSchema)
|
||||
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", colName));
|
||||
|
||||
if (col.HasValue)
|
||||
{
|
||||
colName = col.Value.Name;
|
||||
|
||||
if (col.Value.IsKey)
|
||||
{
|
||||
Where(colName, con.Value);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Values(colName, con.Value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add update fields.</summary>
|
||||
/// <param name="column">Update column and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicUpdateQueryBuilder Values(DynamicColumn column)
|
||||
{
|
||||
if (ValueColumns.ContainsKey(column.ColumnName.ToLower()))
|
||||
ValueColumns[column.ColumnName.ToLower()] = column;
|
||||
else
|
||||
ValueColumns.Add(column.ColumnName.ToLower(), column);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add update fields.</summary>
|
||||
/// <param name="column">Update column.</param>
|
||||
/// <param name="value">Update value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicUpdateQueryBuilder Values(string column, object value)
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
var v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return Values(v);
|
||||
}
|
||||
|
||||
if (ValueColumns.ContainsKey(column.ToLower()))
|
||||
ValueColumns[column.ToLower()].Value = value;
|
||||
else
|
||||
ValueColumns.Add(column.ToLower(), new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Value = value
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add update fields.</summary>
|
||||
/// <param name="values">Set update value as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual DynamicUpdateQueryBuilder Values(object values)
|
||||
{
|
||||
if (values is DynamicColumn)
|
||||
return Values((DynamicColumn)values);
|
||||
|
||||
var dict = values.ToDictionary();
|
||||
var mapper = DynamicMapperCache.GetMapper(values.GetType());
|
||||
|
||||
if (mapper != null)
|
||||
{
|
||||
foreach (var con in dict)
|
||||
if (!mapper.Ignored.Contains(con.Key))
|
||||
Values(mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key, con.Value);
|
||||
}
|
||||
else
|
||||
foreach (var con in dict)
|
||||
Values(con.Key, con.Value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Fill command with query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
public override IDbCommand FillCommand(IDbCommand command)
|
||||
{
|
||||
if (ValueColumns.Count == 0)
|
||||
throw new InvalidOperationException("Update query should contain columns to change.");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
var db = DynamicTable.Database;
|
||||
|
||||
sb.Append("UPDATE ");
|
||||
db.DecorateName(sb, TableName);
|
||||
sb.Append(" SET ");
|
||||
|
||||
bool first = true;
|
||||
|
||||
foreach (var v in ValueColumns)
|
||||
{
|
||||
int pos = command.Parameters.Count;
|
||||
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
|
||||
db.DecorateName(sb, v.Value.ColumnName);
|
||||
sb.Append(" = ");
|
||||
db.GetParameterName(sb, pos);
|
||||
|
||||
command.AddParameter(this, v.Value);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
FillWhere(command, sb);
|
||||
|
||||
return command.SetCommand(sb.ToString());
|
||||
}
|
||||
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public override dynamic Execute()
|
||||
{
|
||||
return DynamicTable.Execute(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
245
DynamORM/Builders/Extensions/DynamicHavingQueryExtensions.cs
Normal file
245
DynamORM/Builders/Extensions/DynamicHavingQueryExtensions.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Extensions
|
||||
{
|
||||
internal static class DynamicHavingQueryExtensions
|
||||
{
|
||||
#region Where
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
return builder.InternalHaving(false, false, func);
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, bool addBeginBrace, bool addEndBrace, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
||||
|
||||
using (DynamicParser parser = DynamicParser.Parse(func))
|
||||
{
|
||||
string condition = null;
|
||||
bool and = true;
|
||||
|
||||
object result = parser.Result;
|
||||
if (result is string)
|
||||
{
|
||||
condition = (string)result;
|
||||
|
||||
if (condition.ToUpper().IndexOf("OR") == 0)
|
||||
{
|
||||
and = false;
|
||||
condition = condition.Substring(3);
|
||||
}
|
||||
else if (condition.ToUpper().IndexOf("AND") == 0)
|
||||
condition = condition.Substring(4);
|
||||
}
|
||||
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||
return builder.InternalHaving(result);
|
||||
else
|
||||
{
|
||||
// Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods...
|
||||
if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument)
|
||||
{
|
||||
DynamicParser.Node.Method node = (DynamicParser.Node.Method)result;
|
||||
string name = node.Name.ToUpper();
|
||||
if (name == "AND" || name == "OR")
|
||||
{
|
||||
object[] args = ((DynamicParser.Node.Method)node).Arguments;
|
||||
if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name));
|
||||
if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch()));
|
||||
|
||||
and = name == "AND" ? true : false;
|
||||
result = args[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Just parsing the contents now...
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (addBeginBrace) builder.HavingOpenBracketsCount++;
|
||||
if (addEndBrace) builder.HavingOpenBracketsCount--;
|
||||
|
||||
if (builder.HavingCondition == null)
|
||||
builder.HavingCondition = string.Format("{0}{1}{2}",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
else
|
||||
builder.HavingCondition = string.Format("{0} {1} {2}{3}{4}", builder.HavingCondition, and ? "AND" : "OR",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
bool virt = builder.VirtualMode;
|
||||
if (column.VirtualColumn.HasValue)
|
||||
builder.VirtualMode = column.VirtualColumn.Value;
|
||||
|
||||
Action<IParameter> modParam = (p) =>
|
||||
{
|
||||
if (column.Schema.HasValue)
|
||||
p.Schema = column.Schema;
|
||||
|
||||
if (!p.Schema.HasValue)
|
||||
p.Schema = column.Schema ?? builder.GetColumnFromSchema(column.ColumnName);
|
||||
};
|
||||
|
||||
builder.CreateTemporaryParameterAction(modParam);
|
||||
|
||||
// It's kind of uglu, but... well it works.
|
||||
if (column.Or)
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
||||
}
|
||||
else
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
||||
}
|
||||
|
||||
builder.OnCreateTemporaryParameter.Remove(modParam);
|
||||
builder.VirtualMode = virt;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
DynamicColumn v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return builder.InternalHaving(v);
|
||||
}
|
||||
else if (value is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)value)
|
||||
builder.InternalHaving(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
return builder.InternalHaving(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Operator = op,
|
||||
Value = value
|
||||
});
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
return builder.InternalHaving(column, DynamicColumn.CompareOperator.Eq, value);
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
return builder.InternalHaving((DynamicColumn)conditions);
|
||||
else if (conditions is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)conditions)
|
||||
builder.InternalHaving(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = conditions.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
string table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty);
|
||||
|
||||
foreach (KeyValuePair<string, object> condition in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table")
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key;
|
||||
|
||||
DynamicSchemaColumn? col = null;
|
||||
|
||||
// This should be used on typed queries or update/delete steatements, which usualy operate on a single table.
|
||||
if (schema)
|
||||
{
|
||||
col = builder.GetColumnFromSchema(colName, mapper, table);
|
||||
|
||||
if ((!col.HasValue || !col.Value.IsKey) &&
|
||||
(mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true)))
|
||||
continue;
|
||||
|
||||
colName = col.HasValue ? col.Value.Name : colName;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(table))
|
||||
builder.InternalHaving(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value);
|
||||
else
|
||||
builder.InternalHaving(x => x(builder.FixObjectName(colName)) == condition.Value);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
}
|
||||
162
DynamORM/Builders/Extensions/DynamicModifyBuilderExtensions.cs
Normal file
162
DynamORM/Builders/Extensions/DynamicModifyBuilderExtensions.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Extensions
|
||||
{
|
||||
internal static class DynamicModifyBuilderExtensions
|
||||
{
|
||||
internal static T Table<T>(this T builder, Func<dynamic, object> func) where T : DynamicModifyBuilder
|
||||
{
|
||||
if (func == null)
|
||||
throw new ArgumentNullException("Function cannot be null.");
|
||||
|
||||
using (DynamicParser parser = DynamicParser.Parse(func))
|
||||
{
|
||||
object result = parser.Result;
|
||||
|
||||
// If the expression result is string.
|
||||
if (result is string)
|
||||
return builder.Table((string)result);
|
||||
else if (result is Type)
|
||||
return builder.Table((Type)result);
|
||||
else if (result is DynamicParser.Node)
|
||||
{
|
||||
// Or if it resolves to a dynamic node
|
||||
DynamicParser.Node node = (DynamicParser.Node)result;
|
||||
|
||||
string owner = null;
|
||||
string main = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Deny support for the AS() virtual method...
|
||||
if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "AS")
|
||||
throw new ArgumentException(string.Format("Alias is not supported on modification builders. (Parsing: {0})", result));
|
||||
|
||||
// Support for table specifications...
|
||||
if (node is DynamicParser.Node.GetMember)
|
||||
{
|
||||
if (owner != null)
|
||||
throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result));
|
||||
|
||||
if (main != null)
|
||||
owner = ((DynamicParser.Node.GetMember)node).Name;
|
||||
else
|
||||
main = ((DynamicParser.Node.GetMember)node).Name;
|
||||
|
||||
node = node.Host;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Support for generic sources...
|
||||
if (node is DynamicParser.Node.Invoke)
|
||||
{
|
||||
if (owner == null && main == null)
|
||||
{
|
||||
DynamicParser.Node.Invoke invoke = (DynamicParser.Node.Invoke)node;
|
||||
|
||||
if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type)
|
||||
return builder.Table((Type)invoke.Arguments[0]);
|
||||
else if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is String)
|
||||
return builder.Table((string)invoke.Arguments[0]);
|
||||
else
|
||||
throw new ArgumentException(string.Format("Invalid argument count or type when parsing '{2}'. Invoke supports only one argument of type Type or String", owner, main, result));
|
||||
}
|
||||
else if (owner != null)
|
||||
throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result));
|
||||
else if (main != null)
|
||||
throw new ArgumentException(string.Format("Main '{0}' is already set when parsing '{1}'.", main, result));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(main))
|
||||
return builder.Table(string.Format("{0}{1}",
|
||||
string.IsNullOrEmpty(owner) ? string.Empty : string.Format("{0}.", owner),
|
||||
main));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException(string.Format("Unable to set table parsing '{0}'", result));
|
||||
}
|
||||
}
|
||||
|
||||
internal static T Table<T>(this T builder, string tableName, Dictionary<string, DynamicSchemaColumn> schema = null) where T : DynamicModifyBuilder
|
||||
{
|
||||
Tuple<string, string> tuple = tableName.Validated("Table Name").SplitSomethingAndAlias();
|
||||
|
||||
if (!string.IsNullOrEmpty(tuple.Item2))
|
||||
throw new ArgumentException(string.Format("Can not use aliases in INSERT steatement. ({0})", tableName), "tableName");
|
||||
|
||||
string[] parts = tuple.Item1.Split('.');
|
||||
|
||||
if (parts.Length > 2)
|
||||
throw new ArgumentException(string.Format("Table name can consist only from name or owner and name. ({0})", tableName), "tableName");
|
||||
|
||||
builder.Tables.Clear();
|
||||
builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database,
|
||||
builder.Database.StripName(parts.Last()).Validated("Table"), null,
|
||||
parts.Length == 2 ? builder.Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null));
|
||||
|
||||
if (schema != null)
|
||||
(builder.Tables[0] as DynamicQueryBuilder.TableInfo).Schema = schema;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T Table<T>(this T builder, Type type) where T : DynamicQueryBuilder
|
||||
{
|
||||
if (type.IsAnonymous())
|
||||
throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}).", type.FullName));
|
||||
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type);
|
||||
|
||||
if (mapper == null)
|
||||
throw new InvalidOperationException("Cant assign unmapable type as a table.");
|
||||
|
||||
if (builder is DynamicModifyBuilder)
|
||||
{
|
||||
builder.Tables.Clear();
|
||||
builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database, type));
|
||||
}
|
||||
else if (builder is DynamicSelectQueryBuilder)
|
||||
(builder as DynamicSelectQueryBuilder).From(x => x(type));
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
245
DynamORM/Builders/Extensions/DynamicWhereQueryExtensions.cs
Normal file
245
DynamORM/Builders/Extensions/DynamicWhereQueryExtensions.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Extensions
|
||||
{
|
||||
internal static class DynamicWhereQueryExtensions
|
||||
{
|
||||
#region Where
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
return builder.InternalWhere(false, false, func);
|
||||
}
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, bool addBeginBrace, bool addEndBrace, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
||||
|
||||
using (DynamicParser parser = DynamicParser.Parse(func))
|
||||
{
|
||||
string condition = null;
|
||||
bool and = true;
|
||||
|
||||
object result = parser.Result;
|
||||
if (result is string)
|
||||
{
|
||||
condition = (string)result;
|
||||
|
||||
if (condition.ToUpper().IndexOf("OR") == 0)
|
||||
{
|
||||
and = false;
|
||||
condition = condition.Substring(3);
|
||||
}
|
||||
else if (condition.ToUpper().IndexOf("AND") == 0)
|
||||
condition = condition.Substring(4);
|
||||
}
|
||||
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||
return builder.InternalWhere(result);
|
||||
else
|
||||
{
|
||||
// Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods...
|
||||
if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument)
|
||||
{
|
||||
DynamicParser.Node.Method node = (DynamicParser.Node.Method)result;
|
||||
string name = node.Name.ToUpper();
|
||||
if (name == "AND" || name == "OR")
|
||||
{
|
||||
object[] args = ((DynamicParser.Node.Method)node).Arguments;
|
||||
if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name));
|
||||
if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch()));
|
||||
|
||||
and = name == "AND" ? true : false;
|
||||
result = args[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Just parsing the contents now...
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (addBeginBrace) builder.WhereOpenBracketsCount++;
|
||||
if (addEndBrace) builder.WhereOpenBracketsCount--;
|
||||
|
||||
if (builder.WhereCondition == null)
|
||||
builder.WhereCondition = string.Format("{0}{1}{2}",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
else
|
||||
builder.WhereCondition = string.Format("{0} {1} {2}{3}{4}", builder.WhereCondition, and ? "AND" : "OR",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
bool virt = builder.VirtualMode;
|
||||
if (column.VirtualColumn.HasValue)
|
||||
builder.VirtualMode = column.VirtualColumn.Value;
|
||||
|
||||
Action<IParameter> modParam = (p) =>
|
||||
{
|
||||
if (column.Schema.HasValue)
|
||||
p.Schema = column.Schema;
|
||||
|
||||
if (!p.Schema.HasValue)
|
||||
p.Schema = column.Schema ?? builder.GetColumnFromSchema(column.ColumnName);
|
||||
};
|
||||
|
||||
builder.CreateTemporaryParameterAction(modParam);
|
||||
|
||||
// It's kind of uglu, but... well it works.
|
||||
if (column.Or)
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
||||
}
|
||||
else
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
||||
}
|
||||
|
||||
builder.OnCreateTemporaryParameter.Remove(modParam);
|
||||
builder.VirtualMode = virt;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
DynamicColumn v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return builder.InternalWhere(v);
|
||||
}
|
||||
else if (value is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)value)
|
||||
builder.InternalWhere(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
return builder.InternalWhere(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Operator = op,
|
||||
Value = value
|
||||
});
|
||||
}
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
return builder.InternalWhere(column, DynamicColumn.CompareOperator.Eq, value);
|
||||
}
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
return builder.InternalWhere((DynamicColumn)conditions);
|
||||
else if (conditions is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)conditions)
|
||||
builder.InternalWhere(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = conditions.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
string table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty);
|
||||
|
||||
foreach (KeyValuePair<string, object> condition in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table")
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key;
|
||||
|
||||
DynamicSchemaColumn? col = null;
|
||||
|
||||
// This should be used on typed queries or update/delete steatements, which usualy operate on a single table.
|
||||
if (schema)
|
||||
{
|
||||
col = builder.GetColumnFromSchema(colName, mapper, table);
|
||||
|
||||
if ((!col.HasValue || !col.Value.IsKey) &&
|
||||
(mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true)))
|
||||
continue;
|
||||
|
||||
colName = col.HasValue ? col.Value.Name : colName;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(table))
|
||||
builder.InternalWhere(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value);
|
||||
else
|
||||
builder.InternalWhere(x => x(builder.FixObjectName(colName)) == condition.Value);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
}
|
||||
78
DynamORM/Builders/IDynamicDeleteQueryBuilder.cs
Normal file
78
DynamORM/Builders/IDynamicDeleteQueryBuilder.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Dynamic delete query builder interface.</summary>
|
||||
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
|
||||
public interface IDynamicDeleteQueryBuilder : IDynamicQueryBuilder
|
||||
{
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Result of an execution..</returns>
|
||||
int Execute();
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicDeleteQueryBuilder Where(Func<dynamic, object> func);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicDeleteQueryBuilder Where(DynamicColumn column);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicDeleteQueryBuilder Where(string column, object value);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicDeleteQueryBuilder Where(object conditions, bool schema = false);
|
||||
}
|
||||
}
|
||||
63
DynamORM/Builders/IDynamicInsertQueryBuilder.cs
Normal file
63
DynamORM/Builders/IDynamicInsertQueryBuilder.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Dynamic insert query builder interface.</summary>
|
||||
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
|
||||
public interface IDynamicInsertQueryBuilder : IDynamicQueryBuilder
|
||||
{
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Result of an execution..</returns>
|
||||
int Execute();
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the columns to insert using the dynamic lambda expressions given. Each expression correspond to one
|
||||
/// column, and can:
|
||||
/// <para>- Resolve to a string, in this case a '=' must appear in the string.</para>
|
||||
/// <para>- Resolve to a expression with the form: 'x => x.Column = Value'.</para>
|
||||
/// </summary>
|
||||
/// <param name="fn">The specifications.</param>
|
||||
/// <param name="func">The specifications.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicInsertQueryBuilder Values(Func<dynamic, object> fn, params Func<dynamic, object>[] func);
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="column">Insert column.</param>
|
||||
/// <param name="value">Insert value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicInsertQueryBuilder Insert(string column, object value);
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicInsertQueryBuilder Insert(object o);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,19 +26,28 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using DynamORM.Helpers;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Base query builder interface.</summary>
|
||||
public interface IDynamicQueryBuilder
|
||||
/// <summary>Dynamic query builder base interface.</summary>
|
||||
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
|
||||
public interface IDynamicQueryBuilder : IExtendedDisposable
|
||||
{
|
||||
/// <summary>Gets <see cref="DynamicTable"/> instance.</summary>
|
||||
DynamicTable DynamicTable { get; }
|
||||
/// <summary>Gets <see cref="DynamicDatabase"/> instance.</summary>
|
||||
DynamicDatabase Database { get; }
|
||||
|
||||
/// <summary>Gets table schema.</summary>
|
||||
Dictionary<string, DynamicSchemaColumn> Schema { get; }
|
||||
/// <summary>Gets tables information.</summary>
|
||||
IList<ITableInfo> Tables { get; }
|
||||
|
||||
/// <summary>Gets the tables used in this builder.</summary>
|
||||
IDictionary<string, IParameter> Parameters { get; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether add virtual parameters.</summary>
|
||||
bool VirtualMode { get; set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
||||
bool SupportSchema { get; }
|
||||
@@ -48,8 +57,19 @@ namespace DynamORM.Builders
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
IDbCommand FillCommand(IDbCommand command);
|
||||
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Result of an execution..</returns>
|
||||
dynamic Execute();
|
||||
/// <summary>
|
||||
/// Generates the text this command will execute against the underlying database.
|
||||
/// </summary>
|
||||
/// <returns>The text to execute against the underlying database.</returns>
|
||||
/// <remarks>This method must be override by derived classes.</remarks>
|
||||
string CommandText();
|
||||
|
||||
/// <summary>Gets or sets the on create temporary parameter actions.</summary>
|
||||
/// <remarks>This is exposed to allow setting schema of column.</remarks>
|
||||
List<Action<IParameter>> OnCreateTemporaryParameter { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the on create real parameter actions.</summary>
|
||||
/// <remarks>This is exposed to allow modification of parameter.</remarks>
|
||||
List<Action<IParameter, IDbDataParameter>> OnCreateParameter { get; set; }
|
||||
}
|
||||
}
|
||||
288
DynamORM/Builders/IDynamicSelectQueryBuilder.cs
Normal file
288
DynamORM/Builders/IDynamicSelectQueryBuilder.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Dynamic select query builder interface.</summary>
|
||||
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
|
||||
public interface IDynamicSelectQueryBuilder : IDynamicQueryBuilder ////, IEnumerable<object>
|
||||
{
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Enumerator of objects expanded from query.</returns>
|
||||
IEnumerable<dynamic> Execute();
|
||||
|
||||
/// <summary>Execute this builder and map to given type.</summary>
|
||||
/// <typeparam name="T">Type of object to map on.</typeparam>
|
||||
/// <returns>Enumerator of objects expanded from query.</returns>
|
||||
IEnumerable<T> Execute<T>() where T : class;
|
||||
|
||||
/// <summary>Execute this builder as a data reader.</summary>
|
||||
/// <param name="reader">Action containing reader.</param>
|
||||
void ExecuteDataReader(Action<IDataReader> reader);
|
||||
|
||||
/// <summary>Returns a single result.</summary>
|
||||
/// <returns>Result of a query.</returns>
|
||||
object Scalar();
|
||||
|
||||
#if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE
|
||||
|
||||
/// <summary>Returns a single result.</summary>
|
||||
/// <typeparam name="T">Type to parse to.</typeparam>
|
||||
/// <param name="defaultValue">Default value.</param>
|
||||
/// <returns>Result of a query.</returns>
|
||||
T ScalarAs<T>(T defaultValue = default(T));
|
||||
|
||||
#endif
|
||||
|
||||
#region From/Join
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'From' clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||
/// formats are:
|
||||
/// <para>- Resolve to a string: 'x => "Table AS Alias', where the alias part is optional.</para>
|
||||
/// <para>- Resolve to an expression: 'x => x.Table.As( x.Alias )', where the alias part is optional.</para>
|
||||
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this
|
||||
/// case the alias is not annotated.</para>
|
||||
/// </summary>
|
||||
/// <param name="fn">The specification.</param>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder From(Func<dynamic, object> fn, params Func<dynamic, object>[] func);
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Join' clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||
/// formats are:
|
||||
/// <para>- Resolve to a string: 'x => "Table AS Alias ON Condition', where the alias part is optional.</para>
|
||||
/// <para>- Resolve to an expression: 'x => x.Table.As( x.Alias ).On( condition )', where the alias part is optional.</para>
|
||||
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias ).On( condition )', where the alias part is mandatory.
|
||||
/// In this case the alias is not annotated.</para>
|
||||
/// The expression might be prepended by a method that, in this case, is used to specify the specific join type you
|
||||
/// want to perform, as in: 'x => x.Left()...". Two considerations apply:
|
||||
/// <para>- If a 'false' argument is used when no 'Join' part appears in its name, then no 'Join' suffix is added
|
||||
/// with a space in between.</para>
|
||||
/// <para>- If a 'false' argument is used when a 'Join' part does appear, then no split is performed to separate the
|
||||
/// 'Join' part.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder Join(params Func<dynamic, object>[] func);
|
||||
|
||||
#endregion From/Join
|
||||
|
||||
#region Where
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder Where(Func<dynamic, object> func);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Where(DynamicColumn column);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Where(string column, object value);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Where(object conditions, bool schema = false);
|
||||
|
||||
#endregion Where
|
||||
|
||||
#region Select
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Select' clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||
/// formats are:
|
||||
/// <para>- Resolve to a string: 'x => "Table.Column AS Alias', where the alias part is optional.</para>
|
||||
/// <para>- Resolve to an expression: 'x => x.Table.Column.As( x.Alias )', where the alias part is optional.</para>
|
||||
/// <para>- Select all columns from a table: 'x => x.Table.All()'.</para>
|
||||
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this case
|
||||
/// the alias is not annotated.</para>
|
||||
/// </summary>
|
||||
/// <param name="fn">The specification.</param>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder Select(Func<dynamic, object> fn, params Func<dynamic, object>[] func);
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to add to object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns);
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to add to object.</param>
|
||||
/// <remarks>Column format consist of <c>Column Name</c>, <c>Alias</c> and
|
||||
/// <c>Aggregate function</c> in this order separated by '<c>:</c>'.</remarks>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder SelectColumn(params string[] columns);
|
||||
|
||||
#endregion Select
|
||||
|
||||
#region GroupBy
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Group By' clause the contents obtained from from parsing the dynamic lambda expression given.
|
||||
/// </summary>
|
||||
/// <param name="fn">The specification.</param>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder GroupBy(Func<dynamic, object> fn, params Func<dynamic, object>[] func);
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to group by.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns);
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to group by.</param>
|
||||
/// <remarks>Column format consist of <c>Column Name</c> and
|
||||
/// <c>Alias</c> in this order separated by '<c>:</c>'.</remarks>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder GroupByColumn(params string[] columns);
|
||||
|
||||
#endregion GroupBy
|
||||
|
||||
#region Having
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Having( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder Having(Func<dynamic, object> func);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(DynamicColumn column);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(string column, object value);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(object conditions, bool schema = false);
|
||||
|
||||
#endregion Having
|
||||
|
||||
#region OrderBy
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Order By' clause the contents obtained from from parsing the dynamic lambda expression given. It
|
||||
/// accepts a multipart column specification followed by an optional <code>Ascending()</code> or <code>Descending()</code> virtual methods
|
||||
/// to specify the direction. If no virtual method is used, the default is ascending order. You can also use the
|
||||
/// shorter versions <code>Asc()</code> and <code>Desc()</code>.
|
||||
/// </summary>
|
||||
/// <param name="fn">The specification.</param>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder OrderBy(Func<dynamic, object> fn, params Func<dynamic, object>[] func);
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to order by.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns);
|
||||
|
||||
/// <summary>Add select columns.</summary>
|
||||
/// <param name="columns">Columns to order by.</param>
|
||||
/// <remarks>Column format consist of <c>Column Name</c> and
|
||||
/// <c>Alias</c> in this order separated by '<c>:</c>'.</remarks>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder OrderByColumn(params string[] columns);
|
||||
|
||||
#endregion OrderBy
|
||||
|
||||
#region Top/Limit/Offset/Distinct
|
||||
|
||||
/// <summary>Set top if database support it.</summary>
|
||||
/// <param name="top">How many objects select.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Top(int? top);
|
||||
|
||||
/// <summary>Set top if database support it.</summary>
|
||||
/// <param name="limit">How many objects select.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Limit(int? limit);
|
||||
|
||||
/// <summary>Set top if database support it.</summary>
|
||||
/// <param name="offset">How many objects skip selecting.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Offset(int? offset);
|
||||
|
||||
/// <summary>Set distinct mode.</summary>
|
||||
/// <param name="distinct">Distinct mode.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Distinct(bool distinct = true);
|
||||
|
||||
#endregion Top/Limit/Offset/Distinct
|
||||
}
|
||||
}
|
||||
122
DynamORM/Builders/IDynamicUpdateQueryBuilder.cs
Normal file
122
DynamORM/Builders/IDynamicUpdateQueryBuilder.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Dynamic update query builder interface.</summary>
|
||||
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
|
||||
public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder
|
||||
{
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Result of an execution..</returns>
|
||||
int Execute();
|
||||
|
||||
#region Update
|
||||
|
||||
/// <summary>Add update value or where condition using schema.</summary>
|
||||
/// <param name="column">Update or where column name.</param>
|
||||
/// <param name="value">Column value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Update(string column, object value);
|
||||
|
||||
/// <summary>Add update values and where condition columns using schema.</summary>
|
||||
/// <param name="conditions">Set values or conditions as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Update(object conditions);
|
||||
|
||||
#endregion Update
|
||||
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the columns to update using the dynamic lambda expressions given. Each expression correspond to one
|
||||
/// column, and can:
|
||||
/// <para>- Resolve to a string, in this case a '=' must appear in the string.</para>
|
||||
/// <para>- Resolve to a expression with the form: 'x => x.Column = Value'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specifications.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicUpdateQueryBuilder Set(params Func<dynamic, object>[] func);
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="column">Insert column.</param>
|
||||
/// <param name="value">Insert value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Values(string column, object value);
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Values(object o);
|
||||
|
||||
#endregion Values
|
||||
|
||||
#region Where
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicUpdateQueryBuilder Where(Func<dynamic, object> func);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Where(DynamicColumn column);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Where(string column, object value);
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicUpdateQueryBuilder Where(object conditions, bool schema = false);
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
}
|
||||
55
DynamORM/Builders/IParameter.cs
Normal file
55
DynamORM/Builders/IParameter.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using DynamORM.Helpers;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Interface describing parameter info.</summary>
|
||||
public interface IParameter : IExtendedDisposable
|
||||
{
|
||||
/// <summary>Gets the parameter position in command.</summary>
|
||||
/// <remarks>Available after filling the command.</remarks>
|
||||
int Ordinal { get; }
|
||||
|
||||
/// <summary>Gets the parameter temporary name.</summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>Gets or sets the parameter value.</summary>
|
||||
object Value { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether name of temporary parameter is well known.</summary>
|
||||
bool WellKnown { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether this <see cref="IParameter"/> is virtual.</summary>
|
||||
bool Virtual { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||
DynamicSchemaColumn? Schema { get; set; }
|
||||
}
|
||||
}
|
||||
52
DynamORM/Builders/ITableInfo.cs
Normal file
52
DynamORM/Builders/ITableInfo.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Helpers;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
{
|
||||
/// <summary>Interface describing table information.</summary>
|
||||
public interface ITableInfo : IExtendedDisposable
|
||||
{
|
||||
/// <summary>Gets table owner name.</summary>
|
||||
string Owner { get; }
|
||||
|
||||
/// <summary>Gets table name.</summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>Gets table alias.</summary>
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>Gets table no lock status.</summary>
|
||||
bool NoLock { get; }
|
||||
|
||||
/// <summary>Gets table schema.</summary>
|
||||
Dictionary<string, DynamicSchemaColumn> Schema { get; }
|
||||
}
|
||||
}
|
||||
129
DynamORM/Builders/Implementation/DynamicDeleteQueryBuilder.cs
Normal file
129
DynamORM/Builders/Implementation/DynamicDeleteQueryBuilder.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders.Extensions;
|
||||
|
||||
namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
/// <summary>Implementation of dynamic delete query builder.</summary>
|
||||
internal class DynamicDeleteQueryBuilder : DynamicModifyBuilder, IDynamicDeleteQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicDeleteQueryBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
internal DynamicDeleteQueryBuilder(DynamicDatabase db)
|
||||
: base(db)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicDeleteQueryBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="tableName">Name of the table.</param>
|
||||
public DynamicDeleteQueryBuilder(DynamicDatabase db, string tableName)
|
||||
: base(db, tableName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Generates the text this command will execute against the underlying database.</summary>
|
||||
/// <returns>The text to execute against the underlying database.</returns>
|
||||
/// <remarks>This method must be override by derived classes.</remarks>
|
||||
public override string CommandText()
|
||||
{
|
||||
ITableInfo info = Tables.Single();
|
||||
return string.Format("DELETE FROM {0}{1}{2}{3}",
|
||||
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||
Database.DecorateName(info.Name),
|
||||
string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ",
|
||||
WhereCondition);
|
||||
}
|
||||
|
||||
#region Where
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
public virtual IDynamicDeleteQueryBuilder Where(Func<dynamic, object> func)
|
||||
{
|
||||
return this.InternalWhere(func);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicDeleteQueryBuilder Where(DynamicColumn column)
|
||||
{
|
||||
return this.InternalWhere(column);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value)
|
||||
{
|
||||
return this.InternalWhere(column, op, value);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicDeleteQueryBuilder Where(string column, object value)
|
||||
{
|
||||
return this.InternalWhere(column, value);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicDeleteQueryBuilder Where(object conditions, bool schema = false)
|
||||
{
|
||||
return this.InternalWhere(conditions, schema);
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
}
|
||||
224
DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs
Normal file
224
DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
/// <summary>Implementation of dynamic insert query builder.</summary>
|
||||
internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder
|
||||
{
|
||||
private string _columns;
|
||||
private string _values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
internal DynamicInsertQueryBuilder(DynamicDatabase db)
|
||||
: base(db)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="tableName">Name of the table.</param>
|
||||
public DynamicInsertQueryBuilder(DynamicDatabase db, string tableName)
|
||||
: base(db, tableName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Generates the text this command will execute against the underlying database.</summary>
|
||||
/// <returns>The text to execute against the underlying database.</returns>
|
||||
/// <remarks>This method must be override by derived classes.</remarks>
|
||||
public override string CommandText()
|
||||
{
|
||||
ITableInfo info = Tables.Single();
|
||||
return string.Format("INSERT INTO {0}{1} ({2}) VALUES ({3})",
|
||||
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||
Database.DecorateName(info.Name), _columns, _values);
|
||||
}
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the columns to insert using the dynamic lambda expressions given. Each expression correspond to one
|
||||
/// column, and can:
|
||||
/// <para>- Resolve to a string, in this case a '=' must appear in the string.</para>
|
||||
/// <para>- Resolve to a expression with the form: 'x => x.Column = Value'.</para>
|
||||
/// </summary>
|
||||
/// <param name="fn">The specifications.</param>
|
||||
/// <param name="func">The specifications.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
public virtual IDynamicInsertQueryBuilder Values(Func<dynamic, object> fn, params Func<dynamic, object>[] func)
|
||||
{
|
||||
if (fn == null)
|
||||
throw new ArgumentNullException("Array of specifications cannot be null.");
|
||||
|
||||
int index = InsertFunc(-1, fn);
|
||||
|
||||
if (func != null)
|
||||
foreach (Func<dynamic, object> f in func)
|
||||
index = InsertFunc(index, f);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private int InsertFunc(int index, Func<dynamic, object> f)
|
||||
{
|
||||
index++;
|
||||
|
||||
if (f == null)
|
||||
throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index));
|
||||
|
||||
using (DynamicParser parser = DynamicParser.Parse(f))
|
||||
{
|
||||
object result = parser.Result;
|
||||
if (result == null)
|
||||
throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index));
|
||||
|
||||
string main = null;
|
||||
string value = null;
|
||||
string str = null;
|
||||
|
||||
// When 'x => x.Table.Column = value' or 'x => x.Column = value'...
|
||||
if (result is DynamicParser.Node.SetMember)
|
||||
{
|
||||
DynamicParser.Node.SetMember node = (DynamicParser.Node.SetMember)result;
|
||||
|
||||
DynamicSchemaColumn? col = GetColumnFromSchema(node.Name);
|
||||
main = Database.DecorateName(node.Name);
|
||||
value = Parse(node.Value, ref col, pars: Parameters, nulls: true);
|
||||
|
||||
_columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main);
|
||||
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||
return index;
|
||||
}
|
||||
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||
{
|
||||
Insert(result);
|
||||
return index;
|
||||
}
|
||||
|
||||
// Other specifications are considered invalid...
|
||||
string err = string.Format("Specification '{0}' is invalid.", result);
|
||||
str = Parse(result);
|
||||
if (str.Contains("=")) err += " May have you used a '==' instead of a '=' operator?";
|
||||
throw new ArgumentException(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="column">Insert column.</param>
|
||||
/// <param name="value">Insert value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicInsertQueryBuilder Insert(string column, object value)
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
DynamicColumn v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return Insert(v);
|
||||
}
|
||||
|
||||
return Insert(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Value = value,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicInsertQueryBuilder Insert(object o)
|
||||
{
|
||||
if (o is DynamicColumn)
|
||||
{
|
||||
DynamicColumn column = (DynamicColumn)o;
|
||||
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
|
||||
|
||||
string main = FixObjectName(column.ColumnName, onlyColumn: true);
|
||||
string value = Parse(column.Value, ref col, pars: Parameters, nulls: true);
|
||||
|
||||
_columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main);
|
||||
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = o.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(o.GetType());
|
||||
|
||||
if (mapper != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> con in dict)
|
||||
if (!mapper.Ignored.Contains(con.Key))
|
||||
{
|
||||
string colName = mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key;
|
||||
DynamicPropertyInvoker propMap = mapper.ColumnsMap.TryGetValue(colName.ToLower());
|
||||
|
||||
if (propMap == null || propMap.Column == null || !propMap.Column.IsNoInsert)
|
||||
Insert(colName, con.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
foreach (KeyValuePair<string, object> con in dict)
|
||||
Insert(con.Key, con.Value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion Insert
|
||||
|
||||
#region IExtendedDisposable
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
_columns = _values = null;
|
||||
}
|
||||
|
||||
#endregion IExtendedDisposable
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,40 +27,46 @@
|
||||
*/
|
||||
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using DynamORM.Builders.Extensions;
|
||||
|
||||
namespace DynamORM.Builders
|
||||
namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
/// <summary>Delete query builder.</summary>
|
||||
public class DynamicDeleteQueryBuilder : DynamicQueryBuilder<DynamicDeleteQueryBuilder>
|
||||
/// <summary>Base query builder for insert/update/delete statements.</summary>
|
||||
internal abstract class DynamicModifyBuilder : DynamicQueryBuilder
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicDeleteQueryBuilder"/> class.</summary>
|
||||
/// <param name="table">Parent dynamic table.</param>
|
||||
public DynamicDeleteQueryBuilder(DynamicTable table)
|
||||
: base(table)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicModifyBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
public DynamicModifyBuilder(DynamicDatabase db)
|
||||
: base(db)
|
||||
{
|
||||
VirtualMode = false;
|
||||
}
|
||||
|
||||
/// <summary>Fill command with query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
public override IDbCommand FillCommand(IDbCommand command)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicModifyBuilder" /> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="tableName">Name of the table.</param>
|
||||
public DynamicModifyBuilder(DynamicDatabase db, string tableName)
|
||||
: this(db)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("DELETE FROM ");
|
||||
DynamicTable.Database.DecorateName(sb, TableName);
|
||||
|
||||
FillWhere(command, sb);
|
||||
|
||||
return command.SetCommand(sb.ToString());
|
||||
VirtualMode = false;
|
||||
this.Table(tableName);
|
||||
}
|
||||
|
||||
/// <summary>Execute this builder.</summary>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public override dynamic Execute()
|
||||
/// <returns>Result of an execution..</returns>
|
||||
public virtual int Execute()
|
||||
{
|
||||
return DynamicTable.Execute(this);
|
||||
using (IDbConnection con = Database.Open())
|
||||
using (IDbCommand cmd = con.CreateCommand())
|
||||
{
|
||||
return cmd
|
||||
.SetCommand(this)
|
||||
.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
997
DynamORM/Builders/Implementation/DynamicQueryBuilder.cs
Normal file
997
DynamORM/Builders/Implementation/DynamicQueryBuilder.cs
Normal file
@@ -0,0 +1,997 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
/// <summary>Implementation of dynamic query builder base interface.</summary>
|
||||
internal abstract class DynamicQueryBuilder : IDynamicQueryBuilder
|
||||
{
|
||||
/// <summary>Empty interface to allow where query builder implementation use universal approach.</summary>
|
||||
internal interface IQueryWithWhere
|
||||
{
|
||||
/// <summary>Gets or sets the where condition.</summary>
|
||||
string WhereCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||
int WhereOpenBracketsCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Empty interface to allow having query builder implementation use universal approach.</summary>
|
||||
internal interface IQueryWithHaving
|
||||
{
|
||||
/// <summary>Gets or sets the having condition.</summary>
|
||||
string HavingCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in having statement.</summary>
|
||||
int HavingOpenBracketsCount { get; set; }
|
||||
}
|
||||
|
||||
private DynamicQueryBuilder _parent = null;
|
||||
|
||||
#region TableInfo
|
||||
|
||||
/// <summary>Table information.</summary>
|
||||
internal class TableInfo : ITableInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableInfo"/> class.
|
||||
/// </summary>
|
||||
internal TableInfo()
|
||||
{
|
||||
IsDisposed = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableInfo" /> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="name">The name of table.</param>
|
||||
/// <param name="alias">The table alias.</param>
|
||||
/// <param name="owner">The table owner.</param>
|
||||
/// <param name="nolock">The table should be used with nolock.</param>
|
||||
public TableInfo(DynamicDatabase db, string name, string alias = null, string owner = null, bool nolock = false)
|
||||
: this()
|
||||
{
|
||||
Name = name;
|
||||
Alias = alias;
|
||||
Owner = owner;
|
||||
NoLock = nolock;
|
||||
|
||||
if (!name.ContainsAny(StringExtensions.InvalidMemberChars))
|
||||
Schema = db.GetSchema(name, owner: owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableInfo" /> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="type">The type which can be mapped to database.</param>
|
||||
/// <param name="alias">The table alias.</param>
|
||||
/// <param name="owner">The table owner.</param>
|
||||
/// <param name="nolock">The table should be used with nolock.</param>
|
||||
public TableInfo(DynamicDatabase db, Type type, string alias = null, string owner = null, bool nolock = false)
|
||||
: this()
|
||||
{
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type);
|
||||
|
||||
Name = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||
mapper.Type.Name : mapper.Table.Name;
|
||||
|
||||
Owner = (mapper.Table != null) ? mapper.Table.Owner : owner;
|
||||
Alias = alias;
|
||||
NoLock = nolock;
|
||||
|
||||
Schema = db.GetSchema(type);
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets table owner name.</summary>
|
||||
public string Owner { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets table name.</summary>
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets table alias.</summary>
|
||||
public string Alias { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets table alias.</summary>
|
||||
public bool NoLock { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets table schema.</summary>
|
||||
public Dictionary<string, DynamicSchemaColumn> Schema { get; internal set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
|
||||
////if (Schema != null)
|
||||
//// Schema.Clear();
|
||||
|
||||
Owner = Name = Alias = null;
|
||||
Schema = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Generic based table information.</summary>
|
||||
/// <typeparam name="T">Type of class that is represented in database.</typeparam>
|
||||
internal class TableInfo<T> : TableInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableInfo{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="alias">The table alias.</param>
|
||||
/// <param name="owner">The table owner.</param>
|
||||
public TableInfo(DynamicDatabase db, string alias = null, string owner = null)
|
||||
: base(db, typeof(T), alias, owner)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion TableInfo
|
||||
|
||||
#region Parameter
|
||||
|
||||
/// <summary>Interface describing parameter info.</summary>
|
||||
internal class Parameter : IParameter
|
||||
{
|
||||
/// <summary>Initializes a new instance of the
|
||||
/// <see cref="Parameter"/> class.</summary>
|
||||
public Parameter()
|
||||
{
|
||||
IsDisposed = false;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the parameter position in command.</summary>
|
||||
/// <remarks>Available after filling the command.</remarks>
|
||||
public int Ordinal { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets the parameter temporary name.</summary>
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets the parameter value.</summary>
|
||||
public object Value { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether name of temporary parameter is well known.</summary>
|
||||
public bool WellKnown { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether this <see cref="Parameter"/> is virtual.</summary>
|
||||
public bool Virtual { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||
public DynamicSchemaColumn? Schema { get; set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
|
||||
Name = null;
|
||||
Schema = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Parameter
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="db">The database.</param>
|
||||
public DynamicQueryBuilder(DynamicDatabase db)
|
||||
{
|
||||
IsDisposed = false;
|
||||
VirtualMode = false;
|
||||
Tables = new List<ITableInfo>();
|
||||
Parameters = new Dictionary<string, IParameter>();
|
||||
OnCreateTemporaryParameter = new List<Action<IParameter>>();
|
||||
OnCreateParameter = new List<Action<IParameter, IDbDataParameter>>();
|
||||
|
||||
WhereCondition = null;
|
||||
WhereOpenBracketsCount = 0;
|
||||
|
||||
Database = db;
|
||||
if (Database != null)
|
||||
Database.AddToCache(this);
|
||||
|
||||
SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
|
||||
SupportNoLock = (db.Options & DynamicDatabaseOptions.SupportNoLock) == DynamicDatabaseOptions.SupportNoLock;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.</summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <param name="parent">The parent query.</param>
|
||||
internal DynamicQueryBuilder(DynamicDatabase db, DynamicQueryBuilder parent)
|
||||
: this(db)
|
||||
{
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
#endregion Constructor
|
||||
|
||||
#region IQueryWithWhere
|
||||
|
||||
/// <summary>Gets or sets the where condition.</summary>
|
||||
public string WhereCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||
public int WhereOpenBracketsCount { get; set; }
|
||||
|
||||
#endregion IQueryWithWhere
|
||||
|
||||
#region IDynamicQueryBuilder
|
||||
|
||||
/// <summary>Gets <see cref="DynamicDatabase"/> instance.</summary>
|
||||
public DynamicDatabase Database { get; private set; }
|
||||
|
||||
/// <summary>Gets the tables used in this builder.</summary>
|
||||
public IList<ITableInfo> Tables { get; private set; }
|
||||
|
||||
/// <summary>Gets the tables used in this builder.</summary>
|
||||
public IDictionary<string, IParameter> Parameters { get; private set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether add virtual parameters.</summary>
|
||||
public bool VirtualMode { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the on create temporary parameter actions.</summary>
|
||||
/// <remarks>This is exposed to allow setting schema of column.</remarks>
|
||||
public List<Action<IParameter>> OnCreateTemporaryParameter { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the on create real parameter actions.</summary>
|
||||
/// <remarks>This is exposed to allow modification of parameter.</remarks>
|
||||
public List<Action<IParameter, IDbDataParameter>> OnCreateParameter { get; set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
||||
public bool SupportSchema { get; private set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether database supports with no lock syntax.</summary>
|
||||
public bool SupportNoLock { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Generates the text this command will execute against the underlying database.
|
||||
/// </summary>
|
||||
/// <returns>The text to execute against the underlying database.</returns>
|
||||
/// <remarks>This method must be override by derived classes.</remarks>
|
||||
public abstract string CommandText();
|
||||
|
||||
/// <summary>Fill command with query.</summary>
|
||||
/// <param name="command">Command to fill.</param>
|
||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||
public virtual IDbCommand FillCommand(IDbCommand command)
|
||||
{
|
||||
// End not ended where statement
|
||||
if (this is IQueryWithWhere)
|
||||
{
|
||||
while (WhereOpenBracketsCount > 0)
|
||||
{
|
||||
WhereCondition += ")";
|
||||
WhereOpenBracketsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
// End not ended having statement
|
||||
if (this is IQueryWithHaving)
|
||||
{
|
||||
IQueryWithHaving h = this as IQueryWithHaving;
|
||||
|
||||
while (h.HavingOpenBracketsCount > 0)
|
||||
{
|
||||
h.HavingCondition += ")";
|
||||
h.HavingOpenBracketsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
return command.SetCommand(CommandText()
|
||||
.FillStringWithVariables(s =>
|
||||
{
|
||||
return Parameters.TryGetValue(s).NullOr(p =>
|
||||
{
|
||||
IDbDataParameter param = (IDbDataParameter)command
|
||||
.AddParameter(this, p.Schema, p.Value)
|
||||
.Parameters[command.Parameters.Count - 1];
|
||||
|
||||
(p as Parameter).Ordinal = command.Parameters.Count - 1;
|
||||
|
||||
if (OnCreateParameter != null)
|
||||
OnCreateParameter.ForEach(x => x(p, param));
|
||||
|
||||
return param.ParameterName;
|
||||
}, s);
|
||||
}));
|
||||
}
|
||||
|
||||
#endregion IDynamicQueryBuilder
|
||||
|
||||
#region Parser
|
||||
|
||||
/// <summary>Parses the arbitrary object given and translates it into a string with the appropriate
|
||||
/// syntax for the database this parser is specific to.</summary>
|
||||
/// <param name="node">The object to parse and translate. It can be any arbitrary object, including null values (if
|
||||
/// permitted) and dynamic lambda expressions.</param>
|
||||
/// <param name="pars">If not null, the parameters' list where to store the parameters extracted by the parsing.</param>
|
||||
/// <param name="rawstr">If true, literal (raw) string are allowed. If false and the node is a literal then, as a
|
||||
/// security measure, an exception is thrown.</param>
|
||||
/// <param name="nulls">True to accept null values and translate them into the appropriate syntax accepted by the
|
||||
/// database. If false and the value is null, then an exception is thrown.</param>
|
||||
/// <param name="decorate">If set to <c>true</c> decorate element.</param>
|
||||
/// <param name="isMultiPart">If set parse argument as alias. This is workaround for AS method.</param>
|
||||
/// <returns>A string containing the result of the parsing, along with the parameters extracted in the
|
||||
/// <paramref name="pars" /> instance if such is given.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Null nodes are not accepted.</exception>
|
||||
internal virtual string Parse(object node, IDictionary<string, IParameter> pars = null, bool rawstr = false, bool nulls = false, bool decorate = true, bool isMultiPart = true)
|
||||
{
|
||||
DynamicSchemaColumn? c = null;
|
||||
|
||||
return Parse(node, ref c, pars, rawstr, nulls, decorate, isMultiPart);
|
||||
}
|
||||
|
||||
/// <summary>Parses the arbitrary object given and translates it into a string with the appropriate
|
||||
/// syntax for the database this parser is specific to.</summary>
|
||||
/// <param name="node">The object to parse and translate. It can be any arbitrary object, including null values (if
|
||||
/// permitted) and dynamic lambda expressions.</param>
|
||||
/// <param name="columnSchema">This parameter is used to determine type of parameter used in query.</param>
|
||||
/// <param name="pars">If not null, the parameters' list where to store the parameters extracted by the parsing.</param>
|
||||
/// <param name="rawstr">If true, literal (raw) string are allowed. If false and the node is a literal then, as a
|
||||
/// security measure, an exception is thrown.</param>
|
||||
/// <param name="nulls">True to accept null values and translate them into the appropriate syntax accepted by the
|
||||
/// database. If false and the value is null, then an exception is thrown.</param>
|
||||
/// <param name="decorate">If set to <c>true</c> decorate element.</param>
|
||||
/// <param name="isMultiPart">If set parse argument as alias. This is workaround for AS method.</param>
|
||||
/// <returns>A string containing the result of the parsing, along with the parameters extracted in the
|
||||
/// <paramref name="pars" /> instance if such is given.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Null nodes are not accepted.</exception>
|
||||
internal virtual string Parse(object node, ref DynamicSchemaColumn? columnSchema, IDictionary<string, IParameter> pars = null, bool rawstr = false, bool nulls = false, bool decorate = true, bool isMultiPart = true)
|
||||
{
|
||||
// Null nodes are accepted or not depending upon the "nulls" flag...
|
||||
if (node == null)
|
||||
{
|
||||
if (!nulls)
|
||||
throw new ArgumentNullException("node", "Null nodes are not accepted.");
|
||||
|
||||
return Dispatch(node, ref columnSchema, pars, decorate);
|
||||
}
|
||||
|
||||
// Nodes that are strings are parametrized or not depending the "rawstr" flag...
|
||||
if (node is string)
|
||||
{
|
||||
if (rawstr) return (string)node;
|
||||
else return Dispatch(node, ref columnSchema, pars, decorate);
|
||||
}
|
||||
|
||||
// If node is a delegate, parse it to create the logical tree...
|
||||
if (node is Delegate)
|
||||
{
|
||||
using (DynamicParser p = DynamicParser.Parse((Delegate)node))
|
||||
{
|
||||
node = p.Result;
|
||||
|
||||
return Parse(node, ref columnSchema, pars, rawstr, decorate: decorate); // Intercept containers as in (x => "string")
|
||||
}
|
||||
}
|
||||
|
||||
return Dispatch(node, ref columnSchema, pars, decorate, isMultiPart);
|
||||
}
|
||||
|
||||
private string Dispatch(object node, ref DynamicSchemaColumn? columnSchema, IDictionary<string, IParameter> pars = null, bool decorate = true, bool isMultiPart = true)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
if (node is DynamicQueryBuilder) return ParseCommand((DynamicQueryBuilder)node, pars);
|
||||
else if (node is DynamicParser.Node.Argument) return ParseArgument((DynamicParser.Node.Argument)node, isMultiPart);
|
||||
else if (node is DynamicParser.Node.GetMember) return ParseGetMember((DynamicParser.Node.GetMember)node, ref columnSchema, pars, decorate, isMultiPart);
|
||||
else if (node is DynamicParser.Node.SetMember) return ParseSetMember((DynamicParser.Node.SetMember)node, ref columnSchema, pars, decorate, isMultiPart);
|
||||
else if (node is DynamicParser.Node.Unary) return ParseUnary((DynamicParser.Node.Unary)node, pars);
|
||||
else if (node is DynamicParser.Node.Binary) return ParseBinary((DynamicParser.Node.Binary)node, pars);
|
||||
else if (node is DynamicParser.Node.Method) return ParseMethod((DynamicParser.Node.Method)node, ref columnSchema, pars);
|
||||
else if (node is DynamicParser.Node.Invoke) return ParseInvoke((DynamicParser.Node.Invoke)node, ref columnSchema, pars);
|
||||
else if (node is DynamicParser.Node.Convert) return ParseConvert((DynamicParser.Node.Convert)node, pars);
|
||||
}
|
||||
|
||||
// All other cases are considered constant parameters...
|
||||
return ParseConstant(node, pars, columnSchema);
|
||||
}
|
||||
|
||||
internal virtual string ParseCommand(DynamicQueryBuilder node, IDictionary<string, IParameter> pars = null)
|
||||
{
|
||||
// Getting the command's text...
|
||||
string str = node.CommandText(); // Avoiding spurious "OUTPUT XXX" statements
|
||||
|
||||
// If there are parameters to transform, but cannot store them, it is an error
|
||||
if (node.Parameters.Count != 0 && pars == null)
|
||||
return string.Format("({0})", str);
|
||||
|
||||
// TODO: Make special condiion
|
||||
////throw new InvalidOperationException(string.Format("The parameters in this command '{0}' cannot be added to a null collection.", node.Parameters));
|
||||
|
||||
// Copy parameters to new comand
|
||||
foreach (KeyValuePair<string, IParameter> parameter in node.Parameters)
|
||||
pars.Add(parameter.Key, parameter.Value);
|
||||
|
||||
return string.Format("({0})", str);
|
||||
}
|
||||
|
||||
protected virtual string ParseArgument(DynamicParser.Node.Argument node, bool isMultiPart = true, bool isOwner = false)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(node.Name) && (isOwner || (isMultiPart && IsTableAlias(node.Name))))
|
||||
return node.Name;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual string ParseGetMember(DynamicParser.Node.GetMember node, ref DynamicSchemaColumn? columnSchema, IDictionary<string, IParameter> pars = null, bool decorate = true, bool isMultiPart = true)
|
||||
{
|
||||
if (node.Host is DynamicParser.Node.Argument && IsTableAlias(node.Name))
|
||||
{
|
||||
decorate = false;
|
||||
isMultiPart = false;
|
||||
}
|
||||
|
||||
// This hack allows to use argument as alias, but when it is not nesesary use other column.
|
||||
// Let say we hace a table Users with alias usr, and we join to table with alias ua which also has a column Users
|
||||
// This allow use of usr => usr.ua.Users to result in ua."Users" instead of "Users" or usr."ua"."Users", se tests for examples.
|
||||
string parent = null;
|
||||
if (node.Host != null)
|
||||
{
|
||||
if (isMultiPart && node.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, null))
|
||||
{
|
||||
if (node.Host.Host != null && node.Host.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, node.Host.Host.Name))
|
||||
parent = string.Format("{0}.{1}", Parse(node.Host.Host, pars, isMultiPart: false), Parse(node.Host, pars, isMultiPart: false));
|
||||
else
|
||||
parent = Parse(node.Host, pars, isMultiPart: false);
|
||||
}
|
||||
else if (isMultiPart)
|
||||
parent = Parse(node.Host, pars, isMultiPart: isMultiPart);
|
||||
}
|
||||
|
||||
////string parent = node.Host == null || !isMultiPart ? null : Parse(node.Host, pars, isMultiPart: !IsTable(node.Name, node.Host.Name));
|
||||
string name = parent == null ?
|
||||
decorate ? Database.DecorateName(node.Name) : node.Name :
|
||||
string.Format("{0}.{1}", parent, decorate ? Database.DecorateName(node.Name) : node.Name);
|
||||
|
||||
columnSchema = GetColumnFromSchema(name);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
protected virtual string ParseSetMember(DynamicParser.Node.SetMember node, ref DynamicSchemaColumn? columnSchema, IDictionary<string, IParameter> pars = null, bool decorate = true, bool isMultiPart = true)
|
||||
{
|
||||
if (node.Host is DynamicParser.Node.Argument && IsTableAlias(node.Name))
|
||||
{
|
||||
decorate = false;
|
||||
isMultiPart = false;
|
||||
}
|
||||
|
||||
string parent = null;
|
||||
if (node.Host != null)
|
||||
{
|
||||
if (isMultiPart && node.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, null))
|
||||
{
|
||||
if (node.Host.Host != null && node.Host.Host is DynamicParser.Node.GetMember && IsTable(node.Name, node.Host.Name))
|
||||
parent = string.Format("{0}.{1}", Parse(node.Host.Host, pars, isMultiPart: false), Parse(node.Host, pars, isMultiPart: false));
|
||||
else
|
||||
parent = Parse(node.Host, pars, isMultiPart: false);
|
||||
}
|
||||
else if (isMultiPart)
|
||||
parent = Parse(node.Host, pars, isMultiPart: isMultiPart);
|
||||
}
|
||||
|
||||
////string parent = node.Host == null || !isMultiPart ? null : Parse(node.Host, pars, isMultiPart: !IsTable(node.Name, node.Host.Name));
|
||||
string name = parent == null ?
|
||||
decorate ? Database.DecorateName(node.Name) : node.Name :
|
||||
string.Format("{0}.{1}", parent, decorate ? Database.DecorateName(node.Name) : node.Name);
|
||||
|
||||
columnSchema = GetColumnFromSchema(name);
|
||||
|
||||
string value = Parse(node.Value, ref columnSchema, pars, nulls: true);
|
||||
return string.Format("{0} = ({1})", name, value);
|
||||
}
|
||||
|
||||
protected virtual string ParseUnary(DynamicParser.Node.Unary node, IDictionary<string, IParameter> pars = null)
|
||||
{
|
||||
switch (node.Operation)
|
||||
{
|
||||
// Artifacts from the DynamicParser class that are not usefull here...
|
||||
case ExpressionType.IsFalse:
|
||||
case ExpressionType.IsTrue: return Parse(node.Target, pars);
|
||||
|
||||
// Unary supported operations...
|
||||
case ExpressionType.Not: return string.Format("(NOT {0})", Parse(node.Target, pars));
|
||||
case ExpressionType.Negate: return string.Format("!({0})", Parse(node.Target, pars));
|
||||
}
|
||||
|
||||
throw new ArgumentException("Not supported unary operation: " + node);
|
||||
}
|
||||
|
||||
protected virtual string ParseBinary(DynamicParser.Node.Binary node, IDictionary<string, IParameter> pars = null)
|
||||
{
|
||||
string op = string.Empty;
|
||||
|
||||
switch (node.Operation)
|
||||
{
|
||||
// Arithmetic binary operations...
|
||||
case ExpressionType.Add: op = "+"; break;
|
||||
case ExpressionType.Subtract: op = "-"; break;
|
||||
case ExpressionType.Multiply: op = "*"; break;
|
||||
case ExpressionType.Divide: op = "/"; break;
|
||||
case ExpressionType.Modulo: op = "%"; break;
|
||||
case ExpressionType.Power: op = "^"; break;
|
||||
|
||||
case ExpressionType.And: op = "AND"; break;
|
||||
case ExpressionType.Or: op = "OR"; break;
|
||||
|
||||
// Logical comparisons...
|
||||
case ExpressionType.GreaterThan: op = ">"; break;
|
||||
case ExpressionType.GreaterThanOrEqual: op = ">="; break;
|
||||
case ExpressionType.LessThan: op = "<"; break;
|
||||
case ExpressionType.LessThanOrEqual: op = "<="; break;
|
||||
|
||||
// Comparisons against 'NULL' require the 'IS' or 'IS NOT' operator instead the numeric ones...
|
||||
case ExpressionType.Equal: op = node.Right == null && !VirtualMode ? "IS" : "="; break;
|
||||
case ExpressionType.NotEqual: op = node.Right == null && !VirtualMode ? "IS NOT" : "<>"; break;
|
||||
|
||||
default: throw new ArgumentException("Not supported operator: '" + node.Operation);
|
||||
}
|
||||
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
string left = Parse(node.Left, ref columnSchema, pars); // Not nulls: left is assumed to be an object
|
||||
string right = Parse(node.Right, ref columnSchema, pars, nulls: true);
|
||||
return string.Format("({0} {1} {2})", left, op, right);
|
||||
}
|
||||
|
||||
protected virtual string ParseMethod(DynamicParser.Node.Method node, ref DynamicSchemaColumn? columnSchema, IDictionary<string, IParameter> pars = null)
|
||||
{
|
||||
string method = node.Name.ToUpper();
|
||||
string parent = node.Host == null ? null : Parse(node.Host, ref columnSchema, pars: pars);
|
||||
string item = null;
|
||||
|
||||
// Root-level methods...
|
||||
if (node.Host == null)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case "NOT":
|
||||
if (node.Arguments == null || node.Arguments.Length != 1) throw new ArgumentNullException("NOT method expects one argument: " + node.Arguments.Sketch());
|
||||
item = Parse(node.Arguments[0], ref columnSchema, pars: pars);
|
||||
return string.Format("(NOT {0})", item);
|
||||
}
|
||||
}
|
||||
|
||||
// Column-level methods...
|
||||
if (node.Host != null)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case "BETWEEN":
|
||||
{
|
||||
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||
throw new ArgumentException("BETWEEN method expects at least one argument: " + node.Arguments.Sketch());
|
||||
|
||||
if (node.Arguments.Length > 2)
|
||||
throw new ArgumentException("BETWEEN method expects at most two arguments: " + node.Arguments.Sketch());
|
||||
|
||||
object[] arguments = node.Arguments;
|
||||
|
||||
if (arguments.Length == 1 && (arguments[0] is IEnumerable<object> || arguments[0] is Array) && !(arguments[0] is byte[]))
|
||||
{
|
||||
IEnumerable<object> vals = arguments[0] as IEnumerable<object>;
|
||||
|
||||
if (vals == null && arguments[0] is Array)
|
||||
vals = ((Array)arguments[0]).Cast<object>() as IEnumerable<object>;
|
||||
|
||||
if (vals != null)
|
||||
arguments = vals.ToArray();
|
||||
else
|
||||
throw new ArgumentException("BETWEEN method expects single argument to be enumerable of exactly two elements: " + node.Arguments.Sketch());
|
||||
}
|
||||
|
||||
return string.Format("{0} BETWEEN {1} AND {2}", parent, Parse(arguments[0], ref columnSchema, pars: pars), Parse(arguments[1], ref columnSchema, pars: pars));
|
||||
}
|
||||
|
||||
case "IN":
|
||||
{
|
||||
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||
throw new ArgumentException("IN method expects at least one argument: " + node.Arguments.Sketch());
|
||||
|
||||
bool firstParam = true;
|
||||
StringBuilder sbin = new StringBuilder();
|
||||
foreach (object arg in node.Arguments)
|
||||
{
|
||||
if (!firstParam)
|
||||
sbin.Append(", ");
|
||||
|
||||
if ((arg is IEnumerable<object> || arg is Array) && !(arg is byte[]))
|
||||
{
|
||||
IEnumerable<object> vals = arg as IEnumerable<object>;
|
||||
|
||||
if (vals == null && arg is Array)
|
||||
vals = ((Array)arg).Cast<object>() as IEnumerable<object>;
|
||||
|
||||
if (vals != null)
|
||||
foreach (object val in vals)
|
||||
{
|
||||
if (!firstParam)
|
||||
sbin.Append(", ");
|
||||
else
|
||||
firstParam = false;
|
||||
|
||||
sbin.Append(Parse(val, ref columnSchema, pars: pars));
|
||||
}
|
||||
else
|
||||
sbin.Append(Parse(arg, ref columnSchema, pars: pars));
|
||||
}
|
||||
else
|
||||
sbin.Append(Parse(arg, ref columnSchema, pars: pars));
|
||||
|
||||
firstParam = false;
|
||||
}
|
||||
|
||||
return string.Format("{0} IN({1})", parent, sbin.ToString());
|
||||
}
|
||||
|
||||
case "NOTIN":
|
||||
{
|
||||
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||
throw new ArgumentException("IN method expects at least one argument: " + node.Arguments.Sketch());
|
||||
|
||||
bool firstParam = true;
|
||||
StringBuilder sbin = new StringBuilder();
|
||||
foreach (object arg in node.Arguments)
|
||||
{
|
||||
if (!firstParam)
|
||||
sbin.Append(", ");
|
||||
|
||||
if ((arg is IEnumerable<object> || arg is Array) && !(arg is byte[]))
|
||||
{
|
||||
IEnumerable<object> vals = arg as IEnumerable<object>;
|
||||
|
||||
if (vals == null && arg is Array)
|
||||
vals = ((Array)arg).Cast<object>() as IEnumerable<object>;
|
||||
|
||||
if (vals != null)
|
||||
foreach (object val in vals)
|
||||
{
|
||||
if (!firstParam)
|
||||
sbin.Append(", ");
|
||||
else
|
||||
firstParam = false;
|
||||
|
||||
sbin.Append(Parse(val, ref columnSchema, pars: pars));
|
||||
}
|
||||
else
|
||||
sbin.Append(Parse(arg, ref columnSchema, pars: pars));
|
||||
}
|
||||
else
|
||||
sbin.Append(Parse(arg, ref columnSchema, pars: pars));
|
||||
|
||||
firstParam = false;
|
||||
}
|
||||
|
||||
return string.Format("{0} NOT IN({1})", parent, sbin.ToString());
|
||||
}
|
||||
|
||||
case "LIKE":
|
||||
if (node.Arguments == null || node.Arguments.Length != 1)
|
||||
throw new ArgumentException("LIKE method expects one argument: " + node.Arguments.Sketch());
|
||||
|
||||
return string.Format("{0} LIKE {1}", parent, Parse(node.Arguments[0], ref columnSchema, pars: pars));
|
||||
|
||||
case "NOTLIKE":
|
||||
if (node.Arguments == null || node.Arguments.Length != 1)
|
||||
throw new ArgumentException("NOT LIKE method expects one argument: " + node.Arguments.Sketch());
|
||||
|
||||
return string.Format("{0} NOT LIKE {1}", parent, Parse(node.Arguments[0], ref columnSchema, pars: pars));
|
||||
|
||||
case "AS":
|
||||
if (node.Arguments == null || node.Arguments.Length != 1)
|
||||
throw new ArgumentException("AS method expects one argument: " + node.Arguments.Sketch());
|
||||
|
||||
item = Parse(node.Arguments[0], pars: null, rawstr: true, isMultiPart: false); // pars=null to avoid to parameterize aliases
|
||||
item = item.Validated("Alias"); // Intercepting null and empty aliases
|
||||
return string.Format("{0} AS {1}", parent, item);
|
||||
|
||||
case "NOLOCK":
|
||||
if (!SupportNoLock)
|
||||
return parent;
|
||||
|
||||
if (node.Arguments != null && node.Arguments.Length > 1)
|
||||
throw new ArgumentException("NOLOCK method expects no arguments.");
|
||||
|
||||
return string.Format("{0} {1}", parent, "WITH(NOLOCK)");
|
||||
|
||||
case "COUNT":
|
||||
if (node.Arguments != null && node.Arguments.Length > 1)
|
||||
throw new ArgumentException("COUNT method expects one or none argument: " + node.Arguments.Sketch());
|
||||
|
||||
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||
return "COUNT(*)";
|
||||
|
||||
return string.Format("COUNT({0})", Parse(node.Arguments[0], ref columnSchema, pars: Parameters, nulls: true));
|
||||
|
||||
case "COUNT0":
|
||||
if (node.Arguments != null && node.Arguments.Length > 0)
|
||||
throw new ArgumentException("COUNT0 method doesn't expect arguments");
|
||||
|
||||
return "COUNT(0)";
|
||||
}
|
||||
}
|
||||
|
||||
// Default case: parsing the method's name along with its arguments...
|
||||
method = parent == null ? node.Name : string.Format("{0}.{1}", parent, node.Name);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("{0}(", method);
|
||||
|
||||
if (node.Arguments != null && node.Arguments.Length != 0)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
foreach (object argument in node.Arguments)
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
sb.Append(Parse(argument, ref columnSchema, pars, nulls: true)); // We don't accept raw strings here!!!
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append(")");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
protected virtual string ParseInvoke(DynamicParser.Node.Invoke node, ref DynamicSchemaColumn? columnSchema, IDictionary<string, IParameter> pars = null)
|
||||
{
|
||||
// This is used as an especial syntax to merely concatenate its arguments. It is used as a way to extend the supported syntax without the need of treating all the possible cases...
|
||||
if (node.Arguments == null || node.Arguments.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (object arg in node.Arguments)
|
||||
{
|
||||
if (arg is string)
|
||||
{
|
||||
sb.Append((string)arg);
|
||||
|
||||
if (node.Arguments.Length == 1 && !columnSchema.HasValue)
|
||||
columnSchema = GetColumnFromSchema((string)arg);
|
||||
}
|
||||
else
|
||||
sb.Append(Parse(arg, ref columnSchema, pars, rawstr: true, nulls: true));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
protected virtual string ParseConvert(DynamicParser.Node.Convert node, IDictionary<string, IParameter> pars = null)
|
||||
{
|
||||
// The cast mechanism is left for the specific database implementation, that should override this method
|
||||
// as needed...
|
||||
string r = Parse(node.Target, pars);
|
||||
return r;
|
||||
}
|
||||
|
||||
protected virtual string ParseConstant(object node, IDictionary<string, IParameter> pars = null, DynamicSchemaColumn? columnSchema = null)
|
||||
{
|
||||
if (node == null && !VirtualMode)
|
||||
return ParseNull();
|
||||
|
||||
if (pars != null)
|
||||
{
|
||||
bool wellKnownName = VirtualMode && node is String && ((String)node).StartsWith("[$") && ((String)node).EndsWith("]") && ((String)node).Length > 4;
|
||||
|
||||
// If we have a list of parameters to store it, let's parametrize it
|
||||
Parameter par = new Parameter()
|
||||
{
|
||||
Name = wellKnownName ? ((String)node).Substring(2, ((String)node).Length - 3) : Guid.NewGuid().ToString(),
|
||||
Value = wellKnownName ? null : node,
|
||||
WellKnown = wellKnownName,
|
||||
Virtual = VirtualMode,
|
||||
Schema = columnSchema,
|
||||
};
|
||||
|
||||
// If we are adding parameter we inform external sources about this.
|
||||
if (OnCreateTemporaryParameter != null)
|
||||
OnCreateTemporaryParameter.ForEach(x => x(par));
|
||||
|
||||
pars.Add(par.Name, par);
|
||||
|
||||
return string.Format("[${0}]", par.Name);
|
||||
}
|
||||
|
||||
return node.ToString(); // Last resort case
|
||||
}
|
||||
|
||||
protected virtual string ParseNull()
|
||||
{
|
||||
return "NULL"; // Override if needed
|
||||
}
|
||||
|
||||
#endregion Parser
|
||||
|
||||
#region Helpers
|
||||
|
||||
internal bool IsTableAlias(string name)
|
||||
{
|
||||
DynamicQueryBuilder builder = this;
|
||||
|
||||
while (builder != null)
|
||||
{
|
||||
if (builder.Tables.Any(t => t.Alias == name))
|
||||
return true;
|
||||
|
||||
builder = builder._parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool IsTable(string name, string owner)
|
||||
{
|
||||
DynamicQueryBuilder builder = this;
|
||||
|
||||
while (builder != null)
|
||||
{
|
||||
if ((string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower())) ||
|
||||
(!string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower() &&
|
||||
!string.IsNullOrEmpty(t.Owner) && t.Owner.ToLower() == owner.ToLower())))
|
||||
return true;
|
||||
|
||||
builder = builder._parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal string FixObjectName(string main, bool onlyColumn = false)
|
||||
{
|
||||
if (main.IndexOf("(") > 0 && main.IndexOf(")") > 0)
|
||||
return main.FillStringWithVariables(f => string.Format("({0})", FixObjectNamePrivate(f, onlyColumn)), "(", ")");
|
||||
else
|
||||
return FixObjectNamePrivate(main, onlyColumn);
|
||||
}
|
||||
|
||||
private string FixObjectNamePrivate(string f, bool onlyColumn = false)
|
||||
{
|
||||
IEnumerable<string> objects = f.Split('.')
|
||||
.Select(x => Database.StripName(x));
|
||||
|
||||
if (onlyColumn || objects.Count() == 1)
|
||||
f = Database.DecorateName(objects.Last());
|
||||
else if (!IsTableAlias(objects.First()))
|
||||
f = string.Join(".", objects.Select(o => Database.DecorateName(o)));
|
||||
else
|
||||
f = string.Format("{0}.{1}", objects.First(), string.Join(".", objects.Skip(1).Select(o => Database.DecorateName(o))));
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
internal DynamicSchemaColumn? GetColumnFromSchema(string colName, DynamicTypeMap mapper = null, string table = null)
|
||||
{
|
||||
// This is tricky and will not always work unfortunetly.
|
||||
////if (colName.ContainsAny(StringExtensions.InvalidMultipartMemberChars))
|
||||
//// return null;
|
||||
|
||||
// First we need to get real column name and it's owner if exist.
|
||||
string[] parts = colName.Split('.');
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
parts[i] = Database.StripName(parts[i]);
|
||||
|
||||
string columnName = parts.Last();
|
||||
|
||||
// Get table name from mapper
|
||||
string tableName = table;
|
||||
|
||||
if (string.IsNullOrEmpty(tableName))
|
||||
{
|
||||
tableName = (mapper != null && mapper.Table != null) ? mapper.Table.Name : string.Empty;
|
||||
|
||||
if (parts.Length > 1 && string.IsNullOrEmpty(tableName))
|
||||
{
|
||||
// OK, we have a multi part identifier, that's good, we can get table name
|
||||
tableName = string.Join(".", parts.Take(parts.Length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get table info from cache
|
||||
ITableInfo tableInfo = !string.IsNullOrEmpty(tableName) ?
|
||||
Tables.FirstOrDefault(x => !string.IsNullOrEmpty(x.Alias) && x.Alias.ToLower() == tableName) ??
|
||||
Tables.FirstOrDefault(x => x.Name.ToLower() == tableName.ToLower()) ?? Tables.FirstOrDefault() :
|
||||
this is DynamicModifyBuilder || Tables.Count == 1 ? Tables.FirstOrDefault() : null;
|
||||
|
||||
// Try to get column from schema
|
||||
if (tableInfo != null && tableInfo.Schema != null)
|
||||
return tableInfo.Schema.TryGetNullable(columnName.ToLower());
|
||||
|
||||
// Well, we failed to find a column
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion Helpers
|
||||
|
||||
#region IExtendedDisposable
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
|
||||
if (Database != null)
|
||||
Database.RemoveFromCache(this);
|
||||
|
||||
if (Parameters != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> p in Parameters)
|
||||
p.Value.Dispose();
|
||||
|
||||
Parameters.Clear();
|
||||
Parameters = null;
|
||||
}
|
||||
|
||||
if (Tables != null)
|
||||
{
|
||||
foreach (ITableInfo t in Tables)
|
||||
if (t != null)
|
||||
t.Dispose();
|
||||
|
||||
Tables.Clear();
|
||||
Tables = null;
|
||||
}
|
||||
|
||||
WhereCondition = null;
|
||||
Database = null;
|
||||
}
|
||||
|
||||
#endregion IExtendedDisposable
|
||||
}
|
||||
}
|
||||
1460
DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs
Normal file
1460
DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs
Normal file
File diff suppressed because it is too large
Load Diff
342
DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs
Normal file
342
DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
/// <summary>Update query builder.</summary>
|
||||
internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
private string _columns;
|
||||
|
||||
internal DynamicUpdateQueryBuilder(DynamicDatabase db)
|
||||
: base(db)
|
||||
{
|
||||
}
|
||||
|
||||
public DynamicUpdateQueryBuilder(DynamicDatabase db, string tableName)
|
||||
: base(db, tableName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Generates the text this command will execute against the underlying database.</summary>
|
||||
/// <returns>The text to execute against the underlying database.</returns>
|
||||
/// <remarks>This method must be override by derived classes.</remarks>
|
||||
public override string CommandText()
|
||||
{
|
||||
ITableInfo info = Tables.Single();
|
||||
return string.Format("UPDATE {0}{1} SET {2}{3}{4}",
|
||||
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||
Database.DecorateName(info.Name), _columns,
|
||||
string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ",
|
||||
WhereCondition);
|
||||
}
|
||||
|
||||
#region Update
|
||||
|
||||
/// <summary>Add update value or where condition using schema.</summary>
|
||||
/// <param name="column">Update or where column name.</param>
|
||||
/// <param name="value">Column value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Update(string column, object value)
|
||||
{
|
||||
DynamicSchemaColumn? col = GetColumnFromSchema(column);
|
||||
|
||||
if (!col.HasValue && SupportSchema)
|
||||
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
|
||||
|
||||
if (col.HasValue && col.Value.IsKey)
|
||||
Where(column, value);
|
||||
else
|
||||
Values(column, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add update values and where condition columns using schema.</summary>
|
||||
/// <param name="conditions">Set values or conditions as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Update(object conditions)
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
{
|
||||
DynamicColumn column = (DynamicColumn)conditions;
|
||||
|
||||
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
|
||||
|
||||
if (!col.HasValue && SupportSchema)
|
||||
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
|
||||
|
||||
if (col.HasValue && col.Value.IsKey)
|
||||
Where(column);
|
||||
else
|
||||
Values(column.ColumnName, column.Value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = conditions.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
|
||||
foreach (KeyValuePair<string, object> con in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(con.Key))
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key : con.Key;
|
||||
DynamicSchemaColumn? col = GetColumnFromSchema(colName);
|
||||
|
||||
if (!col.HasValue && SupportSchema)
|
||||
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", colName));
|
||||
|
||||
if (col.HasValue)
|
||||
{
|
||||
colName = col.Value.Name;
|
||||
|
||||
if (col.Value.IsKey)
|
||||
{
|
||||
Where(colName, con.Value);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DynamicPropertyInvoker propMap = mapper.ColumnsMap.TryGetValue(colName.ToLower());
|
||||
if (propMap == null || propMap.Column == null || !propMap.Column.IsNoUpdate)
|
||||
Values(colName, con.Value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion Update
|
||||
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the columns to update using the dynamic lambda expressions given. Each expression correspond to one
|
||||
/// column, and can:
|
||||
/// <para>- Resolve to a string, in this case a '=' must appear in the string.</para>
|
||||
/// <para>- Resolve to a expression with the form: 'x => x.Column = Value'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specifications.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Set(params Func<dynamic, object>[] func)
|
||||
{
|
||||
if (func == null)
|
||||
throw new ArgumentNullException("Array of specifications cannot be null.");
|
||||
|
||||
int index = -1;
|
||||
foreach (Func<dynamic, object> f in func)
|
||||
{
|
||||
index++;
|
||||
if (f == null)
|
||||
throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index));
|
||||
|
||||
object result = null;
|
||||
|
||||
using (DynamicParser p = DynamicParser.Parse(f))
|
||||
{
|
||||
result = p.Result;
|
||||
|
||||
if (result == null)
|
||||
throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index));
|
||||
|
||||
string main = null;
|
||||
string value = null;
|
||||
string str = null;
|
||||
|
||||
// When 'x => x.Table.Column = value' or 'x => x.Column = value'...
|
||||
if (result is DynamicParser.Node.SetMember)
|
||||
{
|
||||
DynamicParser.Node.SetMember node = (DynamicParser.Node.SetMember)result;
|
||||
|
||||
DynamicSchemaColumn? col = GetColumnFromSchema(node.Name);
|
||||
main = Database.DecorateName(node.Name);
|
||||
value = Parse(node.Value, ref col, pars: Parameters, nulls: true);
|
||||
|
||||
str = string.Format("{0} = {1}", main, value);
|
||||
_columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str);
|
||||
continue;
|
||||
}
|
||||
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||
{
|
||||
Values(result);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Other specifications are considered invalid...
|
||||
string err = string.Format("Specification '{0}' is invalid.", result);
|
||||
str = Parse(result);
|
||||
if (str.Contains("=")) err += " May have you used a '==' instead of a '=' operator?";
|
||||
throw new ArgumentException(err);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="column">Insert column.</param>
|
||||
/// <param name="value">Insert value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Values(string column, object value)
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
DynamicColumn v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return Values(v);
|
||||
}
|
||||
|
||||
return Values(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Value = value,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Add insert fields.</summary>
|
||||
/// <param name="o">Set insert value as properties and values of an object.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Values(object o)
|
||||
{
|
||||
if (o is DynamicColumn)
|
||||
{
|
||||
DynamicColumn column = (DynamicColumn)o;
|
||||
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
|
||||
|
||||
string main = FixObjectName(column.ColumnName, onlyColumn: true);
|
||||
string value = Parse(column.Value, ref col, pars: Parameters, nulls: true);
|
||||
|
||||
string str = string.Format("{0} = {1}", main, value);
|
||||
_columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = o.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(o.GetType());
|
||||
|
||||
if (mapper != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> con in dict)
|
||||
if (!mapper.Ignored.Contains(con.Key))
|
||||
Values(mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key, con.Value);
|
||||
}
|
||||
else
|
||||
foreach (KeyValuePair<string, object> con in dict)
|
||||
Values(con.Key, con.Value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion Values
|
||||
|
||||
#region Where
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Where( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Where(Func<dynamic, object> func)
|
||||
{
|
||||
return this.InternalWhere(func);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Where(DynamicColumn column)
|
||||
{
|
||||
return this.InternalWhere(column);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value)
|
||||
{
|
||||
return this.InternalWhere(column, op, value);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Where(string column, object value)
|
||||
{
|
||||
return this.InternalWhere(column, value);
|
||||
}
|
||||
|
||||
/// <summary>Add where condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicUpdateQueryBuilder Where(object conditions, bool schema = false)
|
||||
{
|
||||
return this.InternalWhere(conditions, schema);
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
|
||||
#region IExtendedDisposable
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
_columns = null;
|
||||
}
|
||||
|
||||
#endregion IExtendedDisposable
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{63963ED7-9C78-4672-A4D4-339B6E825503}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DynamORM</RootNamespace>
|
||||
<AssemblyName>DynamORM</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Builders\DynamicDeleteQueryBuilder.cs" />
|
||||
<Compile Include="Builders\DynamicInsertQueryBuilder.cs" />
|
||||
<Compile Include="Builders\DynamicQueryBuilder.cs" />
|
||||
<Compile Include="Builders\DynamicSelectQueryBuilder.cs" />
|
||||
<Compile Include="Builders\DynamicUpdateQueryBuilder.cs" />
|
||||
<Compile Include="Builders\IDynamicQueryBuilder.cs" />
|
||||
<Compile Include="DynamicColumn.cs" />
|
||||
<Compile Include="DynamicCommand.cs" />
|
||||
<Compile Include="DynamicConnection.cs" />
|
||||
<Compile Include="DynamicDatabase.cs" />
|
||||
<Compile Include="DynamicDatabaseOptions.cs" />
|
||||
<Compile Include="DynamicExtensions.cs" />
|
||||
<Compile Include="DynamicSchemaColumn.cs" />
|
||||
<Compile Include="DynamicTable.cs" />
|
||||
<Compile Include="DynamicTransaction.cs" />
|
||||
<Compile Include="Helpers\CollectionComparer.cs" />
|
||||
<Compile Include="Helpers\FrameworkTools.cs" />
|
||||
<Compile Include="Mapper\ColumnAttribute.cs" />
|
||||
<Compile Include="Mapper\DynamicMapperCache.cs" />
|
||||
<Compile Include="Mapper\DynamicPropertyInvoker.cs" />
|
||||
<Compile Include="Mapper\DynamicTypeMap.cs" />
|
||||
<Compile Include="Mapper\IgnoreAttribute.cs" />
|
||||
<Compile Include="Mapper\TableAttribute.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net472;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<Description>Dynamic Object-Relational Mapping library.</Description>
|
||||
<Copyright>Copyright © RUSSEK Software 2012-2023</Copyright>
|
||||
<Company>RUSSEK Software</Company>
|
||||
<Authors>Grzegorz Russek</Authors>
|
||||
<VersionPrefix>1.5</VersionPrefix>
|
||||
<RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl>
|
||||
<PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl>
|
||||
<Product>DynamORM</Product>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="System.Data.Common" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith('net4')) AND '$(MSBuildRuntimeType)' == 'Core' AND '$(OS)' != 'Windows_NT'">
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
668
DynamORM/DynamicCachedReader.cs
Normal file
668
DynamORM/DynamicCachedReader.cs
Normal file
@@ -0,0 +1,668 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Cache data reader in memory.</summary>
|
||||
public class DynamicCachedReader : DynamicObject, IDataReader
|
||||
{
|
||||
#region Constructor and Data
|
||||
|
||||
private DataTable _schema;
|
||||
private int _fields;
|
||||
private int _rows;
|
||||
private int _position;
|
||||
private int _cachePos;
|
||||
|
||||
private IList<string> _names;
|
||||
private IDictionary<string, int> _ordinals;
|
||||
private IList<Type> _types;
|
||||
private IList<object> _cache;
|
||||
|
||||
private DynamicCachedReader()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicCachedReader" /> class.</summary>
|
||||
/// <param name="reader">Reader to cache.</param>
|
||||
/// <param name="offset">The offset row.</param>
|
||||
/// <param name="limit">The limit to number of tows. -1 is no limit.</param>
|
||||
/// <param name="progress">The progress delegate.</param>
|
||||
public DynamicCachedReader(IDataReader reader, int offset = 0, int limit = -1, Func<DynamicCachedReader, int, bool> progress = null)
|
||||
{
|
||||
InitDataReader(reader, offset, limit, progress);
|
||||
}
|
||||
|
||||
#endregion Constructor and Data
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>Create data reader from dynamic enumerable.</summary>
|
||||
/// <param name="objects">List of objects.</param>
|
||||
/// <returns>Instance of <see cref="DynamicCachedReader"/> containing objects data.</returns>
|
||||
public static DynamicCachedReader FromDynamicEnumerable(IEnumerable<dynamic> objects)
|
||||
{
|
||||
var first = (objects as IEnumerable<dynamic>).FirstOrDefault();
|
||||
|
||||
if (first == null)
|
||||
return null;
|
||||
|
||||
var firstDict = first as IDictionary<string, object>;
|
||||
var r = new DynamicCachedReader();
|
||||
r.Init(firstDict.Keys.Count);
|
||||
|
||||
for (int i = 0; i < firstDict.Keys.Count; i++)
|
||||
r._types.Add(null);
|
||||
|
||||
foreach (dynamic elem in (objects as IEnumerable<dynamic>))
|
||||
{
|
||||
int c = 0;
|
||||
var dict = elem as IDictionary<string, object>;
|
||||
|
||||
foreach (var k in firstDict.Keys)
|
||||
{
|
||||
object val = dict[k];
|
||||
|
||||
r._cache.Add(val);
|
||||
|
||||
if (r._types[c] == null && val != null)
|
||||
r._types[c] = val.GetType();
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
r._rows++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < firstDict.Keys.Count; i++)
|
||||
if (r._types[i] == null)
|
||||
r._types[i] = typeof(string);
|
||||
|
||||
r._schema = new DataTable("DYNAMIC");
|
||||
r._schema.Columns.Add(new DataColumn("ColumnName", typeof(string)));
|
||||
r._schema.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int)));
|
||||
r._schema.Columns.Add(new DataColumn("ColumnSize", typeof(int)));
|
||||
r._schema.Columns.Add(new DataColumn("NumericPrecision", typeof(short)));
|
||||
r._schema.Columns.Add(new DataColumn("NumericScale", typeof(short)));
|
||||
r._schema.Columns.Add(new DataColumn("DataType", typeof(Type)));
|
||||
r._schema.Columns.Add(new DataColumn("ProviderType", typeof(int)));
|
||||
r._schema.Columns.Add(new DataColumn("NativeType", typeof(int)));
|
||||
r._schema.Columns.Add(new DataColumn("AllowDBNull", typeof(bool)));
|
||||
r._schema.Columns.Add(new DataColumn("IsUnique", typeof(bool)));
|
||||
r._schema.Columns.Add(new DataColumn("IsKey", typeof(bool)));
|
||||
r._schema.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool)));
|
||||
|
||||
int ordinal = 0;
|
||||
DataRow dr = null;
|
||||
|
||||
foreach (var column in firstDict.Keys)
|
||||
{
|
||||
dr = r._schema.NewRow();
|
||||
|
||||
dr[0] = column;
|
||||
dr[1] = ordinal;
|
||||
dr[2] = 0;
|
||||
dr[3] = 0;
|
||||
dr[4] = 0;
|
||||
dr[5] = r._types[ordinal];
|
||||
dr[6] = r._types[ordinal].ToDbType();
|
||||
dr[7] = r._types[ordinal].ToDbType();
|
||||
dr[8] = true;
|
||||
dr[9] = false;
|
||||
dr[10] = false;
|
||||
dr[11] = false;
|
||||
|
||||
r._schema.Rows.Add(dr);
|
||||
|
||||
r._names.Add(dr[0].ToString());
|
||||
r._ordinals.Add(dr[0].ToString().ToUpper(), ordinal++);
|
||||
r._types.Add((Type)dr[5]);
|
||||
|
||||
dr.AcceptChanges();
|
||||
}
|
||||
|
||||
dr.AcceptChanges();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>Create data reader from enumerable.</summary>
|
||||
/// <typeparam name="T">Type of enumerated objects.</typeparam>
|
||||
/// <param name="objects">List of objects.</param>
|
||||
/// <returns>Instance of <see cref="DynamicCachedReader"/> containing objects data.</returns>
|
||||
public static DynamicCachedReader FromEnumerable<T>(IEnumerable<T> objects)
|
||||
{
|
||||
var mapper = DynamicMapperCache.GetMapper<T>();
|
||||
|
||||
if (mapper == null)
|
||||
throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", typeof(T).FullName));
|
||||
|
||||
var r = new DynamicCachedReader();
|
||||
r.Init(mapper.ColumnsMap.Count + 1);
|
||||
r.CreateSchemaTable(mapper);
|
||||
r.FillFromEnumerable(objects, mapper);
|
||||
|
||||
r.IsClosed = false;
|
||||
r._position = -1;
|
||||
r._cachePos = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>Create data reader from enumerable.</summary>
|
||||
/// <param name="elementType">Type of enumerated objects.</param>
|
||||
/// <param name="objects">List of objects.</param>
|
||||
/// <returns>Instance of <see cref="DynamicCachedReader"/> containing objects data.</returns>
|
||||
public static DynamicCachedReader FromEnumerable(Type elementType, IEnumerable objects)
|
||||
{
|
||||
var mapper = DynamicMapperCache.GetMapper(elementType);
|
||||
|
||||
if (mapper == null)
|
||||
throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", elementType.FullName));
|
||||
|
||||
var r = new DynamicCachedReader();
|
||||
r.Init(mapper.ColumnsMap.Count + 1);
|
||||
r.CreateSchemaTable(mapper);
|
||||
r.FillFromEnumerable(elementType, objects, mapper);
|
||||
|
||||
r.IsClosed = false;
|
||||
r._position = -1;
|
||||
r._cachePos = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private void InitDataReader(IDataReader reader, int offset = 0, int limit = -1, Func<DynamicCachedReader, int, bool> progress = null)
|
||||
{
|
||||
_schema = reader.GetSchemaTable();
|
||||
RecordsAffected = reader.RecordsAffected;
|
||||
|
||||
Init(reader.FieldCount);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < _fields; i++)
|
||||
{
|
||||
_names.Add(reader.GetName(i));
|
||||
_types.Add(reader.GetFieldType(i));
|
||||
|
||||
if (!_ordinals.ContainsKey(reader.GetName(i).ToUpper()))
|
||||
_ordinals.Add(reader.GetName(i).ToUpper(), i);
|
||||
}
|
||||
|
||||
int current = 0;
|
||||
while (reader.Read())
|
||||
{
|
||||
if (current < offset)
|
||||
{
|
||||
current++;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < _fields; i++)
|
||||
_cache.Add(reader[i]);
|
||||
|
||||
_rows++;
|
||||
current++;
|
||||
|
||||
if (limit >= 0 && _rows >= limit)
|
||||
break;
|
||||
|
||||
if (progress != null && !progress(this, _rows))
|
||||
break;
|
||||
}
|
||||
|
||||
IsClosed = false;
|
||||
_position = -1;
|
||||
_cachePos = -1;
|
||||
|
||||
if (progress != null)
|
||||
progress(this, _rows);
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
private void FillFromEnumerable<T>(IEnumerable<T> objects, DynamicTypeMap mapper)
|
||||
{
|
||||
foreach (var elem in objects)
|
||||
{
|
||||
foreach (var col in mapper.ColumnsMap)
|
||||
{
|
||||
object val = null;
|
||||
|
||||
if (col.Value.Get != null)
|
||||
val = col.Value.Get(elem);
|
||||
|
||||
_cache.Add(val);
|
||||
}
|
||||
|
||||
_cache.Add(elem);
|
||||
|
||||
_rows++;
|
||||
}
|
||||
}
|
||||
|
||||
private void FillFromEnumerable(Type elementType, IEnumerable objects, DynamicTypeMap mapper)
|
||||
{
|
||||
foreach (var elem in objects)
|
||||
{
|
||||
foreach (var col in mapper.ColumnsMap)
|
||||
{
|
||||
object val = null;
|
||||
|
||||
if (col.Value.Get != null)
|
||||
val = col.Value.Get(elem);
|
||||
|
||||
_cache.Add(val);
|
||||
}
|
||||
|
||||
_cache.Add(elem);
|
||||
|
||||
_rows++;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSchemaTable(DynamicTypeMap mapper)
|
||||
{
|
||||
_schema = new DataTable("DYNAMIC");
|
||||
_schema.Columns.Add(new DataColumn("ColumnName", typeof(string)));
|
||||
_schema.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("ColumnSize", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("NumericPrecision", typeof(short)));
|
||||
_schema.Columns.Add(new DataColumn("NumericScale", typeof(short)));
|
||||
_schema.Columns.Add(new DataColumn("DataType", typeof(Type)));
|
||||
_schema.Columns.Add(new DataColumn("ProviderType", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("NativeType", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("AllowDBNull", typeof(bool)));
|
||||
_schema.Columns.Add(new DataColumn("IsUnique", typeof(bool)));
|
||||
_schema.Columns.Add(new DataColumn("IsKey", typeof(bool)));
|
||||
_schema.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool)));
|
||||
|
||||
int ordinal = 0;
|
||||
DataRow dr = null;
|
||||
|
||||
foreach (var column in mapper.ColumnsMap)
|
||||
{
|
||||
dr = _schema.NewRow();
|
||||
|
||||
dr[0] = column.Value.Column.NullOr(x => x.Name ?? column.Value.Name, column.Value.Name);
|
||||
dr[1] = ordinal;
|
||||
dr[2] = column.Value.Column.NullOr(x => x.Size ?? int.MaxValue, int.MaxValue);
|
||||
dr[3] = column.Value.Column.NullOr(x => x.Precision ?? 0, 0);
|
||||
dr[4] = column.Value.Column.NullOr(x => x.Scale ?? 0, 0);
|
||||
dr[5] = column.Value.Column.NullOr(x => x.Type.HasValue ? x.Type.Value.ToType() : column.Value.Type, column.Value.Type);
|
||||
dr[6] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType());
|
||||
dr[7] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType());
|
||||
dr[8] = column.Value.Column.NullOr(x => x.IsKey, false) ? true : column.Value.Column.NullOr(x => x.AllowNull, true);
|
||||
dr[9] = column.Value.Column.NullOr(x => x.IsUnique, false);
|
||||
dr[10] = column.Value.Column.NullOr(x => x.IsKey, false);
|
||||
dr[11] = false;
|
||||
|
||||
_schema.Rows.Add(dr);
|
||||
|
||||
_names.Add(dr[0].ToString());
|
||||
_ordinals.Add(dr[0].ToString().ToUpper(), ordinal++);
|
||||
_types.Add((Type)dr[5]);
|
||||
|
||||
dr.AcceptChanges();
|
||||
}
|
||||
|
||||
dr = _schema.NewRow();
|
||||
|
||||
dr[0] = "#O";
|
||||
dr[1] = ordinal;
|
||||
dr[2] = int.MaxValue;
|
||||
dr[3] = 0;
|
||||
dr[4] = 0;
|
||||
dr[5] = mapper.Type;
|
||||
dr[6] = DbType.Object;
|
||||
dr[7] = DbType.Object;
|
||||
dr[8] = true;
|
||||
dr[9] = false;
|
||||
dr[10] = false;
|
||||
dr[11] = false;
|
||||
|
||||
_schema.Rows.Add(dr);
|
||||
|
||||
_names.Add("#O");
|
||||
_ordinals.Add("#O".ToUpper(), ordinal++);
|
||||
_types.Add(mapper.Type);
|
||||
|
||||
dr.AcceptChanges();
|
||||
}
|
||||
|
||||
private void Init(int fieldCount)
|
||||
{
|
||||
_rows = 0;
|
||||
_fields = fieldCount;
|
||||
_names = new List<string>(_fields);
|
||||
_ordinals = new Dictionary<string, int>(_fields);
|
||||
_types = new List<Type>(_fields);
|
||||
_cache = new List<object>(_fields * 100);
|
||||
}
|
||||
|
||||
/// <summary>Sets the current position in reader.</summary>
|
||||
/// <param name="pos">The position.</param>
|
||||
public void SetPosition(int pos)
|
||||
{
|
||||
if (pos >= -1 && pos < _rows)
|
||||
{
|
||||
_position = pos;
|
||||
_cachePos = _position * _fields;
|
||||
}
|
||||
else
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
#endregion Helpers
|
||||
|
||||
#region IDataReader Members
|
||||
|
||||
/// <summary>Closes the System.Data.IDataReader Object.</summary>
|
||||
public void Close()
|
||||
{
|
||||
IsClosed = true;
|
||||
_position = _rows;
|
||||
_cachePos = -1;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating the depth of nesting for the current row.</summary>
|
||||
/// <remarks>This implementation use this field to indicate row count.</remarks>
|
||||
public int Depth
|
||||
{
|
||||
get { return _rows; }
|
||||
}
|
||||
|
||||
/// <summary>Returns a System.Data.DataTable that describes the column metadata of the
|
||||
/// System.Data.IDataReader.</summary><returns>A System.Data.DataTable that describes
|
||||
/// the column metadata.</returns><exception cref="System.InvalidOperationException">
|
||||
/// The System.Data.IDataReader is closed.</exception>
|
||||
public DataTable GetSchemaTable()
|
||||
{
|
||||
return _schema;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether the data reader is closed.</summary>
|
||||
public bool IsClosed { get; private set; }
|
||||
|
||||
/// <summary>Advances the data reader to the next result, when reading the results of batch SQL statements.</summary>
|
||||
/// <returns>Returns true if there are more rows; otherwise, false.</returns>
|
||||
public bool NextResult()
|
||||
{
|
||||
_cachePos = (++_position) * _fields;
|
||||
|
||||
return _position < _rows;
|
||||
}
|
||||
|
||||
/// <summary>Advances the System.Data.IDataReader to the next record.</summary>
|
||||
/// <returns>Returns true if there are more rows; otherwise, false.</returns>
|
||||
public bool Read()
|
||||
{
|
||||
_cachePos = (++_position) * _fields;
|
||||
|
||||
return _position < _rows;
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.</summary>
|
||||
/// <returns>The number of rows changed, inserted, or deleted; 0 if no rows were affected or the statement
|
||||
/// failed; and -1 for SELECT statements.</returns>
|
||||
public int RecordsAffected { get; private set; }
|
||||
|
||||
#endregion IDataReader Members
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_names.Clear();
|
||||
_types.Clear();
|
||||
_cache.Clear();
|
||||
_schema.Dispose();
|
||||
}
|
||||
|
||||
#endregion IDisposable Members
|
||||
|
||||
#region IDataRecord Members
|
||||
|
||||
/// <summary>Gets the number of columns in the current row.</summary>
|
||||
/// <remarks>When not positioned in a valid record set, 0; otherwise, the number of columns in the current record. The default is -1.</remarks>
|
||||
public int FieldCount { get { return _fields; } }
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public bool GetBoolean(int i)
|
||||
{
|
||||
return (bool)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public byte GetByte(int i)
|
||||
{
|
||||
return (byte)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Reads a stream of bytes from the specified column offset into the buffer
|
||||
/// as an array, starting at the given buffer offset.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <param name="fieldOffset">The index within the field from which to start the read operation.</param>
|
||||
/// <param name="buffer">The buffer into which to read the stream of bytes.</param>
|
||||
/// <param name="bufferoffset">The index for buffer to start the read operation.</param>
|
||||
/// <param name="length">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of bytes read.</returns>
|
||||
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream((byte[])_cache[_cachePos + i]))
|
||||
return ms.Read(buffer, bufferoffset, length);
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public char GetChar(int i)
|
||||
{
|
||||
return (char)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Reads a stream of characters from the specified column offset into the buffer
|
||||
/// as an array, starting at the given buffer offset.</summary>
|
||||
/// <param name="i">The zero-based column ordinal.</param>
|
||||
/// <param name="fieldoffset">The index within the row from which to start the read operation.</param>
|
||||
/// <param name="buffer">The buffer into which to read the stream of bytes.</param>
|
||||
/// <param name="bufferoffset">The index for buffer to start the read operation.</param>
|
||||
/// <param name="length">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of characters read.</returns>
|
||||
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream((byte[])_cache[_cachePos + i]))
|
||||
{
|
||||
byte[] buff = new byte[buffer.Length];
|
||||
long ret = ms.Read(buff, bufferoffset, length);
|
||||
|
||||
for (int n = bufferoffset; n < ret; n++)
|
||||
buffer[n] = (char)buff[n];
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns an System.Data.IDataReader for the specified column ordinal.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>An System.Data.IDataReader.</returns>
|
||||
public IDataReader GetData(int i)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Gets the data type information for the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>The data type information for the specified field.</returns>
|
||||
public string GetDataTypeName(int i)
|
||||
{
|
||||
return _types[i].Name;
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public DateTime GetDateTime(int i)
|
||||
{
|
||||
return (DateTime)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public decimal GetDecimal(int i)
|
||||
{
|
||||
return (decimal)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public double GetDouble(int i)
|
||||
{
|
||||
return (double)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Gets the System.Type information corresponding to the type of System.Object
|
||||
/// that would be returned from System.Data.IDataRecord.GetValue(System.Int32).</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>The System.Type information corresponding to the type of System.Object that
|
||||
/// would be returned from System.Data.IDataRecord.GetValue(System.Int32).</returns>
|
||||
public Type GetFieldType(int i)
|
||||
{
|
||||
return _types[i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public float GetFloat(int i)
|
||||
{
|
||||
return (float)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public Guid GetGuid(int i)
|
||||
{
|
||||
return (Guid)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public short GetInt16(int i)
|
||||
{
|
||||
return (short)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public int GetInt32(int i)
|
||||
{
|
||||
return (int)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public long GetInt64(int i)
|
||||
{
|
||||
return (long)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Gets the name for the field to find.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>The name of the field or the empty string (""), if there is no value to return.</returns>
|
||||
public string GetName(int i)
|
||||
{
|
||||
return _names[i];
|
||||
}
|
||||
|
||||
/// <summary>Return the index of the named field.</summary>
|
||||
/// <param name="name">The name of the field to find.</param>
|
||||
/// <returns>The index of the named field.</returns>
|
||||
public int GetOrdinal(string name)
|
||||
{
|
||||
if (_ordinals.ContainsKey(name.ToUpper()))
|
||||
return _ordinals[name.ToUpper()];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public string GetString(int i)
|
||||
{
|
||||
return (string)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public object GetValue(int i)
|
||||
{
|
||||
return _cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Gets all the attribute fields in the collection for the current record.</summary>
|
||||
/// <param name="values">An array of System.Object to copy the attribute fields into.</param>
|
||||
/// <returns>The number of instances of System.Object in the array.</returns>
|
||||
public int GetValues(object[] values)
|
||||
{
|
||||
for (int i = 0; i < _fields; i++)
|
||||
values[i] = _cache[_cachePos + i];
|
||||
|
||||
return _fields;
|
||||
}
|
||||
|
||||
/// <summary>Return whether the specified field is set to null.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Returns true if the specified field is set to null; otherwise, false.</returns>
|
||||
public bool IsDBNull(int i)
|
||||
{
|
||||
return _cache[_cachePos + i] == null || _cache[_cachePos + i] == DBNull.Value;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets specified value in current record.</summary>
|
||||
/// <param name="name">Name of column.</param>
|
||||
/// <returns>Value of specified column.</returns>
|
||||
public object this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ordinals.ContainsKey(name.ToUpper()))
|
||||
return _cache[_cachePos + _ordinals[name.ToUpper()]];
|
||||
|
||||
throw new IndexOutOfRangeException(String.Format("Field '{0}' not found.", name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets specified value in current record.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Value of specified column.</returns>
|
||||
public object this[int i]
|
||||
{
|
||||
get { return _cache[_cachePos + i]; }
|
||||
}
|
||||
|
||||
#endregion IDataRecord Members
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
|
||||
namespace DynamORM
|
||||
@@ -88,6 +89,7 @@ namespace DynamORM
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicColumn" /> class.</summary>
|
||||
public DynamicColumn()
|
||||
{
|
||||
ParameterDirection = ParameterDirection.Input;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicColumn" /> class.</summary>
|
||||
@@ -129,6 +131,9 @@ namespace DynamORM
|
||||
/// <summary>Gets or sets order direction.</summary>
|
||||
public SortOrder Order { get; set; }
|
||||
|
||||
/// <summary>Gets or sets parameter direction when used in procedure invocation.</summary>
|
||||
public ParameterDirection ParameterDirection { get; set; }
|
||||
|
||||
/// <summary>Gets or sets value for parameters.</summary>
|
||||
public object Value { get; set; }
|
||||
|
||||
@@ -145,7 +150,7 @@ namespace DynamORM
|
||||
public bool EndBlock { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether set parameters for null values.</summary>
|
||||
public bool VirtualColumn { get; set; }
|
||||
public bool? VirtualColumn { get; set; }
|
||||
|
||||
/// <summary>Gets or sets schema representation of a column.</summary>
|
||||
/// <remarks>Workaround to providers issues which sometimes pass wrong
|
||||
@@ -370,7 +375,7 @@ namespace DynamORM
|
||||
/// <summary>Sets the virtual column.</summary>
|
||||
/// <param name="virt">Set virtual column value.</param>
|
||||
/// <returns>Returns self.</returns>
|
||||
public DynamicColumn SetVirtualColumn(bool virt)
|
||||
public DynamicColumn SetVirtualColumn(bool? virt)
|
||||
{
|
||||
VirtualColumn = virt;
|
||||
return this;
|
||||
@@ -390,7 +395,7 @@ namespace DynamORM
|
||||
public static DynamicColumn ParseSelectColumn(string column)
|
||||
{
|
||||
// Split column description
|
||||
var parts = column.Split(':');
|
||||
string[] parts = column.Split(':');
|
||||
|
||||
if (parts.Length > 0)
|
||||
{
|
||||
@@ -416,7 +421,7 @@ namespace DynamORM
|
||||
public static DynamicColumn ParseOrderByColumn(string column)
|
||||
{
|
||||
// Split column description
|
||||
var parts = column.Split(':');
|
||||
string[] parts = column.Split(':');
|
||||
|
||||
if (parts.Length > 0)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,12 +27,14 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using DynamORM.Helpers;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Helper class to easy manage command.</summary>
|
||||
public class DynamicCommand : IDbCommand
|
||||
public class DynamicCommand : IDbCommand, IExtendedDisposable
|
||||
{
|
||||
private IDbCommand _command;
|
||||
private int? _commandTimeout = null;
|
||||
@@ -45,6 +47,7 @@ namespace DynamORM
|
||||
/// <param name="db">The database manager.</param>
|
||||
internal DynamicCommand(DynamicConnection con, DynamicDatabase db)
|
||||
{
|
||||
IsDisposed = false;
|
||||
_con = con;
|
||||
_db = db;
|
||||
|
||||
@@ -55,7 +58,7 @@ namespace DynamORM
|
||||
else
|
||||
{
|
||||
_command = _con.Connection.CreateCommand();
|
||||
_db.CommandsPool[_con.Connection].Add(_command);
|
||||
_db.CommandsPool[_con.Connection].Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +68,7 @@ namespace DynamORM
|
||||
/// <remarks>Used internally to create command without context.</remarks>
|
||||
internal DynamicCommand(DynamicDatabase db)
|
||||
{
|
||||
IsDisposed = false;
|
||||
_db = db;
|
||||
_command = db.Provider.CreateCommand();
|
||||
}
|
||||
@@ -86,7 +90,10 @@ namespace DynamORM
|
||||
////_poolStamp = _db.PoolStamp;
|
||||
}
|
||||
|
||||
return _db.DumpCommands ? _command.Dump(Console.Out) : _command;
|
||||
if (_db.DumpCommands)
|
||||
_db.DumpCommand(_command);
|
||||
|
||||
return _command;
|
||||
}
|
||||
|
||||
#region IDbCommand Members
|
||||
@@ -131,11 +138,13 @@ namespace DynamORM
|
||||
////_poolStamp = 0;
|
||||
_command.Connection = _con.Connection;
|
||||
}
|
||||
else
|
||||
else if (value == null)
|
||||
{
|
||||
_command.Transaction = null;
|
||||
_command.Connection = null;
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException("Can't assign direct IDbConnection implementation. This property accepts only DynamORM implementation of IDbConnection.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +161,14 @@ namespace DynamORM
|
||||
/// <returns>The number of rows affected.</returns>
|
||||
public int ExecuteNonQuery()
|
||||
{
|
||||
return PrepareForExecution().ExecuteNonQuery();
|
||||
try
|
||||
{
|
||||
return PrepareForExecution().ExecuteNonQuery();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DynamicQueryException(ex, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Executes the <see cref="P:System.Data.IDbCommand.CommandText"/>
|
||||
@@ -164,7 +180,14 @@ namespace DynamORM
|
||||
/// <returns>An <see cref="T:System.Data.IDataReader"/> object.</returns>
|
||||
public IDataReader ExecuteReader(CommandBehavior behavior)
|
||||
{
|
||||
return PrepareForExecution().ExecuteReader(behavior);
|
||||
try
|
||||
{
|
||||
return PrepareForExecution().ExecuteReader(behavior);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DynamicQueryException(ex, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Executes the <see cref="P:System.Data.IDbCommand.CommandText"/>
|
||||
@@ -173,7 +196,14 @@ namespace DynamORM
|
||||
/// <returns>An <see cref="T:System.Data.IDataReader"/> object.</returns>
|
||||
public IDataReader ExecuteReader()
|
||||
{
|
||||
return PrepareForExecution().ExecuteReader();
|
||||
try
|
||||
{
|
||||
return PrepareForExecution().ExecuteReader();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DynamicQueryException(ex, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Executes the query, and returns the first column of the
|
||||
@@ -182,7 +212,14 @@ namespace DynamORM
|
||||
/// <returns>The first column of the first row in the result set.</returns>
|
||||
public object ExecuteScalar()
|
||||
{
|
||||
return PrepareForExecution().ExecuteScalar();
|
||||
try
|
||||
{
|
||||
return PrepareForExecution().ExecuteScalar();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DynamicQueryException(ex, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the <see cref="T:System.Data.IDataParameterCollection"/>.</summary>
|
||||
@@ -194,13 +231,20 @@ namespace DynamORM
|
||||
/// <summary>Creates a prepared (or compiled) version of the command on the data source.</summary>
|
||||
public void Prepare()
|
||||
{
|
||||
_command.Prepare();
|
||||
try
|
||||
{
|
||||
_command.Prepare();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DynamicQueryException("Error preparing command.", ex, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the transaction within which the Command
|
||||
/// object of a data provider executes.</summary>
|
||||
/// <remarks>It's does nothing, transaction is peeked from transaction
|
||||
/// pool of a connection.</remarks>
|
||||
/// pool of a connection. This is only a dummy.</remarks>
|
||||
public IDbTransaction Transaction { get { return null; } set { } }
|
||||
|
||||
/// <summary>Gets or sets how command results are applied to the <see cref="T:System.Data.DataRow"/>
|
||||
@@ -214,7 +258,7 @@ namespace DynamORM
|
||||
|
||||
#endregion IDbCommand Members
|
||||
|
||||
#region IDisposable Members
|
||||
#region IExtendedDisposable Members
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
@@ -224,16 +268,27 @@ namespace DynamORM
|
||||
{
|
||||
if (_con != null)
|
||||
{
|
||||
var pool = _db.CommandsPool.TryGetValue(_con.Connection);
|
||||
List<IDbCommand> pool = _db.CommandsPool.TryGetValue(_con.Connection);
|
||||
|
||||
if (pool != null && pool.Contains(_command))
|
||||
pool.Remove(_command);
|
||||
if (pool != null && pool.Contains(this))
|
||||
pool.Remove(this);
|
||||
}
|
||||
|
||||
_command.Dispose();
|
||||
IsDisposed = true;
|
||||
|
||||
if (_command != null)
|
||||
{
|
||||
_command.Parameters.Clear();
|
||||
|
||||
_command.Dispose();
|
||||
_command = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion IDisposable Members
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
#endregion IExtendedDisposable Members
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,13 +28,14 @@
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using DynamORM.Helpers;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Connection wrapper.</summary>
|
||||
/// <remarks>This class is only connection holder, connection is managed by
|
||||
/// <see cref="DynamicDatabase"/> instance.</remarks>
|
||||
public class DynamicConnection : IDbConnection, IDisposable
|
||||
public class DynamicConnection : IDbConnection, IExtendedDisposable
|
||||
{
|
||||
private DynamicDatabase _db;
|
||||
private bool _singleTransaction;
|
||||
@@ -48,6 +49,7 @@ namespace DynamORM
|
||||
/// <param name="singleTransaction">Are we using single transaction mode? I so... act correctly.</param>
|
||||
internal DynamicConnection(DynamicDatabase db, IDbConnection con, bool singleTransaction)
|
||||
{
|
||||
IsDisposed = false;
|
||||
_db = db;
|
||||
Connection = con;
|
||||
_singleTransaction = singleTransaction;
|
||||
@@ -55,11 +57,12 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Begins a database transaction.</summary>
|
||||
/// <param name="il">One of the <see cref="System.Data.IsolationLevel"/> values.</param>
|
||||
/// <param name="custom">Custom parameter describing transaction options.</param>
|
||||
/// <param name="disposed">This action is invoked when transaction is disposed.</param>
|
||||
/// <returns>Returns <see cref="DynamicTransaction"/> representation.</returns>
|
||||
internal DynamicTransaction BeginTransaction(IsolationLevel? il, Action disposed)
|
||||
internal DynamicTransaction BeginTransaction(IsolationLevel? il, object custom, Action disposed)
|
||||
{
|
||||
return new DynamicTransaction(_db, this, _singleTransaction, il, disposed);
|
||||
return new DynamicTransaction(_db, this, _singleTransaction, il, disposed, null);
|
||||
}
|
||||
|
||||
#region IDbConnection Members
|
||||
@@ -75,7 +78,7 @@ namespace DynamORM
|
||||
/// <returns>Returns <see cref="DynamicTransaction"/> representation.</returns>
|
||||
public IDbTransaction BeginTransaction()
|
||||
{
|
||||
return BeginTransaction(null, null);
|
||||
return BeginTransaction(null, null, null);
|
||||
}
|
||||
|
||||
/// <summary>Begins a database transaction with the specified
|
||||
@@ -84,7 +87,16 @@ namespace DynamORM
|
||||
/// <returns>Returns <see cref="DynamicTransaction"/> representation.</returns>
|
||||
public IDbTransaction BeginTransaction(IsolationLevel il)
|
||||
{
|
||||
return BeginTransaction(il, null);
|
||||
return BeginTransaction(il, null, null);
|
||||
}
|
||||
|
||||
/// <summary>Begins a database transaction with the specified
|
||||
/// <see cref="System.Data.IsolationLevel"/> value.</summary>
|
||||
/// <param name="custom">Custom parameter describing transaction options.</param>
|
||||
/// <returns>Returns <see cref="DynamicTransaction"/> representation.</returns>
|
||||
public IDbTransaction BeginTransaction(object custom)
|
||||
{
|
||||
return BeginTransaction(null, custom, null);
|
||||
}
|
||||
|
||||
/// <summary>Changes the current database for an open Connection object.</summary>
|
||||
@@ -136,7 +148,7 @@ namespace DynamORM
|
||||
/// to be used after a connection is opened.</summary>
|
||||
public string Database
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
get { return Connection.Database; }
|
||||
}
|
||||
|
||||
/// <summary>Gets the current state of the connection.</summary>
|
||||
@@ -147,11 +159,19 @@ namespace DynamORM
|
||||
|
||||
#endregion IDbConnection Members
|
||||
|
||||
#region IExtendedDisposable Members
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with freeing,
|
||||
/// releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Close(Connection);
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
#endregion IExtendedDisposable Members
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -50,11 +50,17 @@ namespace DynamORM
|
||||
/// <summary>Database supports limit offset syntax (SELECT ... FROM ... LIMIT x OFFSET y).</summary>
|
||||
SupportLimitOffset = 0x00000040,
|
||||
|
||||
/// <summary>Database supports limit offset syntax (SELECT FIRST x SKIP y ... FROM ...).</summary>
|
||||
SupportFirstSkip = 0x00000020,
|
||||
|
||||
/// <summary>Database support standard schema.</summary>
|
||||
SupportSchema = 0x00000010,
|
||||
|
||||
/// <summary>Database support stored procedures (EXEC procedure ...).</summary>
|
||||
SupportStoredProcedures = 0x00000020,
|
||||
SupportStoredProcedures = 0x00000100,
|
||||
|
||||
/// <summary>Database support with no lock syntax.</summary>
|
||||
SupportNoLock = 0x00001000,
|
||||
|
||||
/// <summary>Debug option allowing to enable command dumps by default.</summary>
|
||||
DumpCommands = 0x01000000,
|
||||
|
||||
204
DynamORM/DynamicExpando.cs
Normal file
204
DynamORM/DynamicExpando.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Dynamic expando is a simple and temporary class to resolve memory leaks inside ExpandoObject.</summary>
|
||||
public class DynamicExpando : DynamicObject, IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable
|
||||
{
|
||||
/// <summary>Class containing information about last accessed property of dynamic object.</summary>
|
||||
public class PropertyAccess
|
||||
{
|
||||
/// <summary>Enum describing type of access to object.</summary>
|
||||
public enum TypeOfAccess
|
||||
{
|
||||
/// <summary>Get member.</summary>
|
||||
Get,
|
||||
|
||||
/// <summary>Set member.</summary>
|
||||
Set,
|
||||
}
|
||||
|
||||
/// <summary>Gets the type of operation.</summary>
|
||||
public TypeOfAccess Operation { get; internal set; }
|
||||
|
||||
/// <summary>Gets the name of property.</summary>
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>Gets the type from binder.</summary>
|
||||
public Type RequestedType { get; internal set; }
|
||||
|
||||
/// <summary>Gets the type of value stored in object.</summary>
|
||||
public Type Type { get; internal set; }
|
||||
|
||||
/// <summary>Gets the value stored in object.</summary>
|
||||
public object Value { get; internal set; }
|
||||
|
||||
/// <summary>Gets the last access time.</summary>
|
||||
public long Ticks { get; internal set; }
|
||||
}
|
||||
|
||||
private Dictionary<string, object> _data = new Dictionary<string, object>();
|
||||
|
||||
private PropertyAccess _lastProp = new PropertyAccess();
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicExpando"/> class.</summary>
|
||||
public DynamicExpando()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Gets the last accesses property.</summary>
|
||||
/// <returns>Description of last accessed property.</returns>
|
||||
public PropertyAccess GetLastAccessesProperty()
|
||||
{
|
||||
return _lastProp;
|
||||
}
|
||||
|
||||
/// <summary>Tries to get member value.</summary>
|
||||
/// <returns>Returns <c>true</c>, if get member was tried, <c>false</c> otherwise.</returns>
|
||||
/// <param name="binder">The context binder.</param>
|
||||
/// <param name="result">The invocation result.</param>
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
result = _data.TryGetValue(binder.Name);
|
||||
|
||||
_lastProp.Operation = PropertyAccess.TypeOfAccess.Get;
|
||||
_lastProp.RequestedType = binder.ReturnType;
|
||||
_lastProp.Name = binder.Name;
|
||||
_lastProp.Value = result;
|
||||
_lastProp.Type = result == null ? typeof(void) : result.GetType();
|
||||
_lastProp.Ticks = DateTime.Now.Ticks;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Tries to set member.</summary>
|
||||
/// <returns>Returns <c>true</c>, if set member was tried, <c>false</c> otherwise.</returns>
|
||||
/// <param name="binder">The context binder.</param>
|
||||
/// <param name="value">Value which will be set.</param>
|
||||
public override bool TrySetMember(SetMemberBinder binder, object value)
|
||||
{
|
||||
_data[binder.Name] = value;
|
||||
|
||||
_lastProp.Operation = PropertyAccess.TypeOfAccess.Set;
|
||||
_lastProp.RequestedType = binder.ReturnType;
|
||||
_lastProp.Name = binder.Name;
|
||||
_lastProp.Value = value;
|
||||
_lastProp.Type = value == null ? typeof(void) : value.GetType();
|
||||
_lastProp.Ticks = DateTime.Now.Ticks;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IDictionary implementation
|
||||
|
||||
bool IDictionary<string, object>.ContainsKey(string key)
|
||||
{
|
||||
return _data.ContainsKey(key);
|
||||
}
|
||||
|
||||
void IDictionary<string, object>.Add(string key, object value)
|
||||
{
|
||||
_data.Add(key, value);
|
||||
}
|
||||
|
||||
bool IDictionary<string, object>.Remove(string key)
|
||||
{
|
||||
return _data.Remove(key);
|
||||
}
|
||||
|
||||
bool IDictionary<string, object>.TryGetValue(string key, out object value)
|
||||
{
|
||||
return _data.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
object IDictionary<string, object>.this[string index] { get { return _data[index]; } set { _data[index] = value; } }
|
||||
|
||||
ICollection<string> IDictionary<string, object>.Keys { get { return _data.Keys; } }
|
||||
|
||||
ICollection<object> IDictionary<string, object>.Values { get { return _data.Values; } }
|
||||
|
||||
#endregion IDictionary implementation
|
||||
|
||||
#region ICollection implementation
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
||||
{
|
||||
((ICollection<KeyValuePair<string, object>>)_data).Add(item);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.Clear()
|
||||
{
|
||||
_data.Clear();
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
||||
{
|
||||
return ((ICollection<KeyValuePair<string, object>>)_data).Contains(item);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
||||
{
|
||||
((ICollection<KeyValuePair<string, object>>)_data).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
||||
{
|
||||
return ((ICollection<KeyValuePair<string, object>>)_data).Remove(item);
|
||||
}
|
||||
|
||||
int ICollection<KeyValuePair<string, object>>.Count { get { return _data.Count; } }
|
||||
|
||||
bool ICollection<KeyValuePair<string, object>>.IsReadOnly { get { return ((ICollection<KeyValuePair<string, object>>)_data).IsReadOnly; } }
|
||||
|
||||
#endregion ICollection implementation
|
||||
|
||||
#region IEnumerable implementation
|
||||
|
||||
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
|
||||
{
|
||||
return _data.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion IEnumerable implementation
|
||||
|
||||
#region IEnumerable implementation
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable)_data).GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion IEnumerable implementation
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
454
DynamORM/DynamicProcedureInvoker.cs
Normal file
454
DynamORM/DynamicProcedureInvoker.cs
Normal file
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Dynamic procedure invoker.</summary>
|
||||
/// <remarks>Unfortunately I can use <c>out</c> and <c>ref</c> to
|
||||
/// return parameters, <see href="http://stackoverflow.com/questions/2475310/c-sharp-4-0-dynamic-doesnt-set-ref-out-arguments"/>.
|
||||
/// But see example for workaround. If there aren't any return parameters execution will return scalar value.
|
||||
/// Scalar result is not converted to provided generic type (if any). For output results there is possibility to map to provided class.
|
||||
/// </remarks><example>You still can use out, return and both way parameters by providing variable prefix:<code>
|
||||
/// dynamic res = db.Procedures.sp_Test_Scalar_In_Out(inp: Guid.NewGuid(), out_outp: Guid.Empty);
|
||||
/// Console.Out.WriteLine(res.outp);</code>
|
||||
/// Prefixes: <c>out_</c>, <c>ret_</c>, <c>both_</c>. Result will contain field without prefix.
|
||||
/// Here is an example with result class:<code>
|
||||
/// public class ProcResult { [Column("outp")] public Guid Output { get; set; } }
|
||||
/// ProcResult res4 = db.Procedures.sp_Test_Scalar_In_Out<ProcResult>(inp: Guid.NewGuid(), out_outp: Guid.Empty) as ProcResult;
|
||||
/// </code>As you can se, you can use mapper to do job for you.</example>
|
||||
public class DynamicProcedureInvoker : DynamicObject, IDisposable
|
||||
{
|
||||
private DynamicDatabase _db;
|
||||
private List<string> _prefixes;
|
||||
|
||||
internal DynamicProcedureInvoker(DynamicDatabase db, List<string> prefixes = null)
|
||||
{
|
||||
_prefixes = prefixes;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
/// <summary>This is where the magic begins.</summary>
|
||||
/// <param name="binder">Binder to create owner.</param>
|
||||
/// <param name="result">Binder invoke result.</param>
|
||||
/// <returns>Returns <c>true</c> if invoke was performed.</returns>
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
List<string> pref = new List<string>();
|
||||
|
||||
if (_prefixes != null)
|
||||
pref.AddRange(_prefixes);
|
||||
|
||||
pref.Add(binder.Name);
|
||||
|
||||
result = new DynamicProcedureInvoker(_db, pref);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>This is where the magic begins.</summary>
|
||||
/// <param name="binder">Binder to invoke.</param>
|
||||
/// <param name="args">Binder arguments.</param>
|
||||
/// <param name="result">Binder invoke result.</param>
|
||||
/// <returns>Returns <c>true</c> if invoke was performed.</returns>
|
||||
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
|
||||
{
|
||||
// parse the method
|
||||
CallInfo info = binder.CallInfo;
|
||||
|
||||
// Get generic types
|
||||
IList<Type> types = binder.GetGenericTypeArguments();
|
||||
|
||||
Dictionary<string, int> retParams = null;
|
||||
|
||||
using (IDbConnection con = _db.Open())
|
||||
using (IDbCommand cmd = con.CreateCommand())
|
||||
{
|
||||
if (_prefixes == null || _prefixes.Count == 0)
|
||||
cmd.SetCommand(CommandType.StoredProcedure, binder.Name);
|
||||
else
|
||||
cmd.SetCommand(CommandType.StoredProcedure, string.Format("{0}.{1}", string.Join(".", _prefixes), binder.Name));
|
||||
|
||||
#region Prepare arguments
|
||||
|
||||
int alen = args.Length;
|
||||
bool retIsAdded = false;
|
||||
|
||||
if (alen > 0)
|
||||
{
|
||||
for (int i = 0; i < alen; i++)
|
||||
{
|
||||
object arg = args[i];
|
||||
|
||||
if (arg is DynamicExpando)
|
||||
cmd.AddParameters(_db, (DynamicExpando)arg);
|
||||
else if (arg is ExpandoObject)
|
||||
cmd.AddParameters(_db, (ExpandoObject)arg);
|
||||
else if (arg is DynamicColumn)
|
||||
{
|
||||
var dcv = (DynamicColumn)arg;
|
||||
|
||||
string paramName = i.ToString();
|
||||
bool isOut = false;
|
||||
bool isRet = false;
|
||||
bool isBoth = false;
|
||||
|
||||
if (info.ArgumentNames.Count > i)
|
||||
{
|
||||
isOut = info.ArgumentNames[i].StartsWith("out_");
|
||||
isRet = info.ArgumentNames[i].StartsWith("ret_");
|
||||
isBoth = info.ArgumentNames[i].StartsWith("both_");
|
||||
|
||||
paramName = isOut || isRet ?
|
||||
info.ArgumentNames[i].Substring(4) :
|
||||
isBoth ? info.ArgumentNames[i].Substring(5) :
|
||||
info.ArgumentNames[i];
|
||||
}
|
||||
|
||||
paramName = dcv.Alias ?? dcv.ColumnName ??
|
||||
(dcv.Schema.HasValue ? dcv.Schema.Value.Name : null) ??
|
||||
paramName;
|
||||
|
||||
if (!isOut && !isRet && !isBoth)
|
||||
{
|
||||
isOut = dcv.ParameterDirection == ParameterDirection.Output;
|
||||
isRet = dcv.ParameterDirection == ParameterDirection.ReturnValue;
|
||||
isBoth = dcv.ParameterDirection == ParameterDirection.InputOutput;
|
||||
}
|
||||
|
||||
if (isRet)
|
||||
retIsAdded = true;
|
||||
|
||||
if (isOut || isRet || isBoth)
|
||||
{
|
||||
if (retParams == null)
|
||||
retParams = new Dictionary<string, int>();
|
||||
retParams.Add(paramName, cmd.Parameters.Count);
|
||||
}
|
||||
|
||||
if (dcv.Schema != null)
|
||||
{
|
||||
var ds = dcv.Schema.Value;
|
||||
cmd.AddParameter(
|
||||
_db.GetParameterName(paramName),
|
||||
isOut ? ParameterDirection.Output :
|
||||
isRet ? ParameterDirection.ReturnValue :
|
||||
isBoth ? ParameterDirection.InputOutput :
|
||||
ParameterDirection.Input,
|
||||
ds.Type, ds.Size, ds.Precision, ds.Scale,
|
||||
(isOut || isRet) ? DBNull.Value : dcv.Value);
|
||||
}
|
||||
else
|
||||
cmd.AddParameter(
|
||||
_db.GetParameterName(paramName),
|
||||
isOut ? ParameterDirection.Output :
|
||||
isRet ? ParameterDirection.ReturnValue :
|
||||
isBoth ? ParameterDirection.InputOutput :
|
||||
ParameterDirection.Input,
|
||||
arg == null ? DbType.String : arg.GetType().ToDbType(),
|
||||
isRet ? 4 : 0,
|
||||
(isOut || isRet) ? DBNull.Value : dcv.Value);
|
||||
}
|
||||
else if (arg is DynamicSchemaColumn)
|
||||
{
|
||||
var dsc = (DynamicSchemaColumn)arg;
|
||||
|
||||
string paramName = i.ToString();
|
||||
bool isOut = false;
|
||||
bool isRet = false;
|
||||
bool isBoth = false;
|
||||
|
||||
if (info.ArgumentNames.Count > i)
|
||||
{
|
||||
isOut = info.ArgumentNames[i].StartsWith("out_");
|
||||
isRet = info.ArgumentNames[i].StartsWith("ret_");
|
||||
isBoth = info.ArgumentNames[i].StartsWith("both_");
|
||||
|
||||
paramName = isOut || isRet ?
|
||||
info.ArgumentNames[i].Substring(4) :
|
||||
isBoth ? info.ArgumentNames[i].Substring(5) :
|
||||
info.ArgumentNames[i];
|
||||
}
|
||||
|
||||
paramName = dsc.Name ?? paramName;
|
||||
|
||||
if (isRet)
|
||||
retIsAdded = true;
|
||||
|
||||
if (isOut || isRet || isBoth)
|
||||
{
|
||||
if (retParams == null)
|
||||
retParams = new Dictionary<string, int>();
|
||||
retParams.Add(paramName, cmd.Parameters.Count);
|
||||
}
|
||||
|
||||
cmd.AddParameter(
|
||||
_db.GetParameterName(paramName),
|
||||
isOut ? ParameterDirection.Output :
|
||||
isRet ? ParameterDirection.ReturnValue :
|
||||
isBoth ? ParameterDirection.InputOutput :
|
||||
ParameterDirection.Input,
|
||||
dsc.Type, dsc.Size, dsc.Precision, dsc.Scale,
|
||||
DBNull.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info.ArgumentNames.Count > i && !string.IsNullOrEmpty(info.ArgumentNames[i]))
|
||||
{
|
||||
bool isOut = info.ArgumentNames[i].StartsWith("out_");
|
||||
bool isRet = info.ArgumentNames[i].StartsWith("ret_");
|
||||
bool isBoth = info.ArgumentNames[i].StartsWith("both_");
|
||||
|
||||
if (isRet)
|
||||
retIsAdded = true;
|
||||
|
||||
string paramName = isOut || isRet ?
|
||||
info.ArgumentNames[i].Substring(4) :
|
||||
isBoth ? info.ArgumentNames[i].Substring(5) :
|
||||
info.ArgumentNames[i];
|
||||
|
||||
if (isOut || isBoth || isRet)
|
||||
{
|
||||
if (retParams == null)
|
||||
retParams = new Dictionary<string, int>();
|
||||
retParams.Add(paramName, cmd.Parameters.Count);
|
||||
}
|
||||
|
||||
cmd.AddParameter(
|
||||
_db.GetParameterName(paramName),
|
||||
isOut ? ParameterDirection.Output :
|
||||
isRet ? ParameterDirection.ReturnValue :
|
||||
isBoth ? ParameterDirection.InputOutput :
|
||||
ParameterDirection.Input,
|
||||
arg == null ? isRet ? DbType.Int32 : DbType.String : arg.GetType().ToDbType(),
|
||||
isRet ? 4 : 0,
|
||||
(isOut || isRet) ? DBNull.Value : arg);
|
||||
}
|
||||
else
|
||||
cmd.AddParameter(_db, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Prepare arguments
|
||||
|
||||
#region Get main result
|
||||
|
||||
object mainResult = null;
|
||||
|
||||
if (types.Count > 0)
|
||||
{
|
||||
mainResult = types[0].GetDefaultValue();
|
||||
|
||||
if (types[0] == typeof(IDataReader))
|
||||
{
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
mainResult = rdr.CachedReader();
|
||||
}
|
||||
else if (types[0] == typeof(DataTable))
|
||||
{
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
mainResult = rdr.CachedReader().ToDataTable(binder.Name);
|
||||
}
|
||||
else if (types[0].IsGenericEnumerable())
|
||||
{
|
||||
Type argType = types[0].GetGenericArguments().First();
|
||||
if (argType == typeof(object))
|
||||
{
|
||||
IDataReader cache = null;
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
cache = rdr.CachedReader();
|
||||
|
||||
mainResult = cache.EnumerateReader().ToList();
|
||||
}
|
||||
else if (argType.IsValueType || argType == typeof(string))
|
||||
{
|
||||
Type listType = typeof(List<>).MakeGenericType(new Type[] { argType });
|
||||
IList listInstance = (IList)Activator.CreateInstance(listType);
|
||||
|
||||
object defVal = listType.GetDefaultValue();
|
||||
|
||||
IDataReader cache = null;
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
cache = rdr.CachedReader();
|
||||
|
||||
while (cache.Read())
|
||||
listInstance.Add(cache[0] != null && cache[0] != DBNull.Value ? argType.CastObject(cache[0]) : defVal);
|
||||
|
||||
mainResult = listInstance;
|
||||
}
|
||||
else if (argType == typeof(Guid))
|
||||
{
|
||||
Type listType = typeof(List<>).MakeGenericType(new Type[] { argType });
|
||||
IList listInstance = (IList)Activator.CreateInstance(listType);
|
||||
|
||||
object defVal = listType.GetDefaultValue();
|
||||
|
||||
IDataReader cache = null;
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
cache = rdr.CachedReader();
|
||||
|
||||
while (cache.Read())
|
||||
{
|
||||
if (cache[0] != null && cache[0] != DBNull.Value && Guid.TryParse(cache[0].ToString(), out Guid g))
|
||||
listInstance.Add(g);
|
||||
}
|
||||
|
||||
mainResult = listInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(argType);
|
||||
if (mapper == null)
|
||||
throw new InvalidCastException(string.Format("Don't know what to do with this type: '{0}'.", argType.ToString()));
|
||||
|
||||
IDataReader cache = null;
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
cache = rdr.CachedReader();
|
||||
|
||||
var lt = typeof(List<>);
|
||||
var ltc = lt.MakeGenericType(argType);
|
||||
var instance = Activator.CreateInstance(ltc) as IList;
|
||||
|
||||
foreach (var item in cache.EnumerateReader())
|
||||
instance.Add(DynamicExtensions.Map(item, argType));
|
||||
|
||||
mainResult = instance;
|
||||
|
||||
//mainResult = cache.EnumerateReader().MapEnumerable(argType).ToList();
|
||||
}
|
||||
}
|
||||
else if (types[0].IsValueType || types[0] == typeof(string))
|
||||
{
|
||||
mainResult = cmd.ExecuteScalar();
|
||||
if (mainResult != null && mainResult != DBNull.Value)
|
||||
mainResult = types[0].CastObject(mainResult);
|
||||
}
|
||||
else if (types[0] == typeof(Guid))
|
||||
{
|
||||
mainResult = cmd.ExecuteScalar();
|
||||
if (mainResult != null && mainResult != DBNull.Value && Guid.TryParse(mainResult.ToString(), out Guid g))
|
||||
mainResult = g;
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(types[0]);
|
||||
if (mapper == null)
|
||||
throw new InvalidCastException(string.Format("Don't know what to do with this type: '{0}'.", types[0].ToString()));
|
||||
|
||||
using (IDataReader rdr = cmd.ExecuteReader())
|
||||
if (rdr.Read())
|
||||
mainResult = (rdr.RowToDynamic() as object).Map(types[0]);
|
||||
else
|
||||
mainResult = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var returnName = _db.GetParameterName("___result___");
|
||||
if (!retIsAdded)
|
||||
cmd.AddParameter(returnName, ParameterDirection.ReturnValue, DbType.Int32, 4, 0, 0, DBNull.Value);
|
||||
|
||||
mainResult = cmd.ExecuteNonQuery();
|
||||
|
||||
IDbDataParameter returnParam = null;
|
||||
if (!retIsAdded)
|
||||
returnParam = cmd.Parameters[returnName] as IDbDataParameter;
|
||||
else
|
||||
{
|
||||
foreach (var e in cmd.Parameters)
|
||||
{
|
||||
var p = e as IDbDataParameter;
|
||||
if (p != null && p.Direction == ParameterDirection.ReturnValue)
|
||||
{
|
||||
returnParam = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnParam != null && returnParam.Value != null && returnParam.Value != DBNull.Value)
|
||||
mainResult = returnParam.Value;
|
||||
}
|
||||
|
||||
#endregion Get main result
|
||||
|
||||
#region Handle out params
|
||||
|
||||
if (retParams != null)
|
||||
{
|
||||
Dictionary<string, object> res = new Dictionary<string, object>();
|
||||
|
||||
if (mainResult != null)
|
||||
{
|
||||
if (mainResult == DBNull.Value)
|
||||
res.Add(binder.Name, null);
|
||||
else
|
||||
res.Add(binder.Name, mainResult);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, int> pos in retParams)
|
||||
res.Add(pos.Key, ((IDbDataParameter)cmd.Parameters[pos.Value]).Value);
|
||||
|
||||
if (types.Count > 1)
|
||||
{
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(types[1]);
|
||||
|
||||
if (mapper != null)
|
||||
result = mapper.Create(res.ToDynamic());
|
||||
else
|
||||
result = res.ToDynamic();
|
||||
}
|
||||
else
|
||||
result = res.ToDynamic();
|
||||
}
|
||||
else
|
||||
result = mainResult;
|
||||
|
||||
#endregion Handle out params
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
102
DynamORM/DynamicQueryException.cs
Normal file
102
DynamORM/DynamicQueryException.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Dynamic query exception.</summary>
|
||||
[Serializable]
|
||||
public class DynamicQueryException : Exception, ISerializable
|
||||
{
|
||||
/// <summary>Initializes a new instance of the
|
||||
/// <see cref="DynamicQueryException"/> class.</summary>
|
||||
/// <param name="command">The command which failed.</param>
|
||||
public DynamicQueryException(IDbCommand command = null)
|
||||
: base(string.Format("Error executing command.{0}{1}", Environment.NewLine, command != null ? command.DumpToString() : string.Empty))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the
|
||||
/// <see cref="DynamicQueryException"/> class.</summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="command">The command which failed.</param>
|
||||
public DynamicQueryException(string message, IDbCommand command = null)
|
||||
: base(string.Format("{0}{1}{2}", message, Environment.NewLine, command != null ? command.DumpToString() : string.Empty))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the
|
||||
/// <see cref="DynamicQueryException"/> class.</summary>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
/// <param name="command">The command which failed.</param>
|
||||
public DynamicQueryException(Exception innerException, IDbCommand command = null)
|
||||
: base(string.Format("Error executing command.{0}{1}", Environment.NewLine, command != null ? command.DumpToString() : string.Empty), innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the
|
||||
/// <see cref="DynamicQueryException"/> class.</summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
/// <param name="command">The command which failed.</param>
|
||||
public DynamicQueryException(string message, Exception innerException, IDbCommand command = null)
|
||||
: base(string.Format("{0}{1}{2}", message, Environment.NewLine, command != null ? command.DumpToString() : string.Empty), innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the
|
||||
/// <see cref="DynamicQueryException"/> class.</summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" />
|
||||
/// that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" />
|
||||
/// that contains contextual information about the source or destination.</param>
|
||||
public DynamicQueryException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>When overridden in a derived class, sets the
|
||||
/// <see cref="T:System.Runtime.Serialization.SerializationInfo" />
|
||||
/// with information about the exception.</summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" />
|
||||
/// that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" />
|
||||
/// that contains contextual information about the source or destination.</param>
|
||||
/// <PermissionSet>
|
||||
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*" />
|
||||
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter" />
|
||||
/// </PermissionSet>
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -45,6 +45,9 @@ namespace DynamORM
|
||||
/// <summary>Gets or sets a value indicating whether column should have unique value.</summary>
|
||||
public bool IsUnique { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether column allows null or not.</summary>
|
||||
public bool AllowNull { get; set; }
|
||||
|
||||
/// <summary>Gets or sets column size.</summary>
|
||||
public int Size { get; set; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,15 +28,19 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
#if !DYNAMORM_OMMIT_OLDSYNTAX
|
||||
|
||||
/// <summary>Dynamic table is a simple ORM using dynamic objects.</summary>
|
||||
/// <example>
|
||||
/// <para>Assume that we have a table representing Users class.</para>
|
||||
@@ -191,7 +195,7 @@ namespace DynamORM
|
||||
/// });</code>
|
||||
/// <code>x.Delete(where: new { id = 14, code = 14 });</code>
|
||||
/// </example>
|
||||
public class DynamicTable : DynamicObject, IDisposable, ICloneable
|
||||
public class DynamicTable : DynamicObject, IExtendedDisposable, ICloneable
|
||||
{
|
||||
private static HashSet<string> _allowedCommands = new HashSet<string>
|
||||
{
|
||||
@@ -211,6 +215,20 @@ namespace DynamORM
|
||||
/// <summary>Gets name of table.</summary>
|
||||
public virtual string TableName { get; private set; }
|
||||
|
||||
/// <summary>Gets name of owner.</summary>
|
||||
public virtual string OwnerName { get; private set; }
|
||||
|
||||
/// <summary>Gets full name of table containing owner and table name.</summary>
|
||||
public virtual string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.IsNullOrEmpty(TableName) ? null : string.IsNullOrEmpty(OwnerName) ?
|
||||
Database.DecorateName(TableName) :
|
||||
string.Format("{0}.{1}", Database.DecorateName(OwnerName), Database.DecorateName(TableName));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets table schema.</summary>
|
||||
/// <remarks>If database doesn't support schema, only key columns are listed here.</remarks>
|
||||
public virtual Dictionary<string, DynamicSchemaColumn> Schema { get; private set; }
|
||||
@@ -222,11 +240,14 @@ namespace DynamORM
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicTable" /> class.</summary>
|
||||
/// <param name="database">Database and connection management.</param>
|
||||
/// <param name="table">Table name.</param>
|
||||
/// <param name="owner">Owner of the table.</param>
|
||||
/// <param name="keys">Override keys in schema.</param>
|
||||
public DynamicTable(DynamicDatabase database, string table = "", string[] keys = null)
|
||||
public DynamicTable(DynamicDatabase database, string table = "", string owner = "", string[] keys = null)
|
||||
{
|
||||
IsDisposed = false;
|
||||
Database = database;
|
||||
TableName = table;
|
||||
TableName = Database.StripName(table);
|
||||
OwnerName = owner != null ? Database.StripName(owner) : string.Empty;
|
||||
TableType = null;
|
||||
|
||||
BuildAndCacheSchema(keys);
|
||||
@@ -241,15 +262,20 @@ namespace DynamORM
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type", "Type can't be null.");
|
||||
|
||||
Database = database;
|
||||
IsDisposed = false;
|
||||
|
||||
Database = database;
|
||||
TableType = type;
|
||||
|
||||
var mapper = DynamicMapperCache.GetMapper(type);
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type);
|
||||
|
||||
if (mapper != null)
|
||||
{
|
||||
TableName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||
type.Name : mapper.Table.Name;
|
||||
OwnerName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Owner) ?
|
||||
null : mapper.Table.Owner;
|
||||
}
|
||||
|
||||
BuildAndCacheSchema(keys);
|
||||
}
|
||||
@@ -261,17 +287,17 @@ namespace DynamORM
|
||||
Dictionary<string, DynamicSchemaColumn> schema = null;
|
||||
|
||||
schema = Database.GetSchema(TableType) ??
|
||||
Database.GetSchema(TableName);
|
||||
Database.GetSchema(TableName, OwnerName);
|
||||
|
||||
#region Fill currrent table schema
|
||||
|
||||
if (keys == null && TableType != null)
|
||||
{
|
||||
var mapper = DynamicMapperCache.GetMapper(TableType);
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(TableType);
|
||||
|
||||
if (mapper != null)
|
||||
{
|
||||
var k = mapper.ColumnsMap.Where(p => p.Value.Column != null && p.Value.Column.IsKey).Select(p => p.Key);
|
||||
IEnumerable<string> k = mapper.ColumnsMap.Where(p => p.Value.Column != null && p.Value.Column.IsKey).Select(p => p.Key);
|
||||
if (k.Count() > 0)
|
||||
keys = k.ToArray();
|
||||
}
|
||||
@@ -320,16 +346,7 @@ namespace DynamORM
|
||||
/// <returns>Enumerator of objects expanded from query.</returns>
|
||||
public virtual IEnumerable<dynamic> Query(string sql, params object[] args)
|
||||
{
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
using (var rdr = cmd
|
||||
.SetCommand(sql)
|
||||
.AddParameters(Database, args)
|
||||
.ExecuteReader())
|
||||
while (rdr.Read())
|
||||
yield return rdr.RowToDynamic();
|
||||
}
|
||||
return Database.Query(sql, args);
|
||||
}
|
||||
|
||||
/// <summary>Enumerate the reader and yield the result.</summary>
|
||||
@@ -337,22 +354,20 @@ namespace DynamORM
|
||||
/// <returns>Enumerator of objects expanded from query.</returns>
|
||||
public virtual IEnumerable<dynamic> Query(IDynamicQueryBuilder builder)
|
||||
{
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
using (var rdr = cmd
|
||||
.SetCommand(builder)
|
||||
.ExecuteReader())
|
||||
while (rdr.Read())
|
||||
yield return rdr.RowToDynamic();
|
||||
}
|
||||
return Database.Query(builder);
|
||||
}
|
||||
|
||||
/// <summary>Create new <see cref="DynamicSelectQueryBuilder"/>.</summary>
|
||||
/// <returns>New <see cref="DynamicSelectQueryBuilder"/> instance.</returns>
|
||||
public virtual DynamicSelectQueryBuilder Query()
|
||||
public virtual IDynamicSelectQueryBuilder Query()
|
||||
{
|
||||
return new DynamicSelectQueryBuilder(this);
|
||||
IDynamicSelectQueryBuilder builder = new DynamicSelectQueryBuilder(this.Database);
|
||||
|
||||
string name = this.FullName;
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
builder.From(x => name);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>Returns a single result.</summary>
|
||||
@@ -363,13 +378,7 @@ namespace DynamORM
|
||||
/// <returns>Result of a query.</returns>
|
||||
public virtual object Scalar(string sql, params object[] args)
|
||||
{
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
return cmd
|
||||
.SetCommand(sql).AddParameters(Database, args)
|
||||
.ExecuteScalar();
|
||||
}
|
||||
return Database.Scalar(sql, args);
|
||||
}
|
||||
|
||||
/// <summary>Returns a single result.</summary>
|
||||
@@ -377,31 +386,68 @@ namespace DynamORM
|
||||
/// <returns>Result of a query.</returns>
|
||||
public virtual object Scalar(IDynamicQueryBuilder builder)
|
||||
{
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
return cmd
|
||||
.SetCommand(builder)
|
||||
.ExecuteScalar();
|
||||
}
|
||||
return Database.Scalar(builder);
|
||||
}
|
||||
|
||||
#if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE
|
||||
|
||||
/// <summary>Returns a single result.</summary>
|
||||
/// <typeparam name="T">What kind of result is expected.</typeparam>
|
||||
/// <param name="sql">SQL query containing numbered parameters in format provided by
|
||||
/// <see cref="DynamicDatabase.GetParameterName(object)"/> methods. Also names should be formatted with
|
||||
/// <see cref="DynamicDatabase.DecorateName(string)"/> method.</param>
|
||||
/// <param name="args">Arguments (parameters).</param>
|
||||
/// <returns>Result of a query.</returns>
|
||||
public virtual T ScalarAs<T>(string sql, params object[] args)
|
||||
{
|
||||
return Database.ScalarAs<T>(sql, args);
|
||||
}
|
||||
|
||||
/// <summary>Returns a single result.</summary>
|
||||
/// <typeparam name="T">What kind of result is expected.</typeparam>
|
||||
/// <param name="builder">Command builder.</param>
|
||||
/// <param name="defaultValue">Default value.</param>
|
||||
/// <returns>Result of a query.</returns>
|
||||
public virtual T ScalarAs<T>(IDynamicQueryBuilder builder, T defaultValue = default(T))
|
||||
{
|
||||
return Database.ScalarAs<T>(builder, defaultValue);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>Execute stored procedure.</summary>
|
||||
/// <param name="procName">Name of stored procedure to execute.</param>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Procedure(string procName)
|
||||
{
|
||||
return Database.Procedure(procName);
|
||||
}
|
||||
|
||||
/// <summary>Execute stored procedure.</summary>
|
||||
/// <param name="procName">Name of stored procedure to execute.</param>
|
||||
/// <param name="args">Arguments (parameters) in form of expando object.</param>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Procedure(string procName, ExpandoObject args = null)
|
||||
public virtual int Procedure(string procName, params object[] args)
|
||||
{
|
||||
if ((Database.Options & DynamicDatabaseOptions.SupportStoredProcedures) != DynamicDatabaseOptions.SupportStoredProcedures)
|
||||
throw new InvalidOperationException("Database connection desn't support stored procedures.");
|
||||
return Database.Procedure(procName, args);
|
||||
}
|
||||
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
return cmd
|
||||
.SetCommand(CommandType.StoredProcedure, procName).AddParameters(Database, args)
|
||||
.ExecuteNonQuery();
|
||||
}
|
||||
/// <summary>Execute stored procedure.</summary>
|
||||
/// <param name="procName">Name of stored procedure to execute.</param>
|
||||
/// <param name="args">Arguments (parameters) in form of expando object.</param>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Procedure(string procName, DynamicExpando args)
|
||||
{
|
||||
return Database.Procedure(procName, args);
|
||||
}
|
||||
|
||||
/// <summary>Execute stored procedure.</summary>
|
||||
/// <param name="procName">Name of stored procedure to execute.</param>
|
||||
/// <param name="args">Arguments (parameters) in form of expando object.</param>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Procedure(string procName, ExpandoObject args)
|
||||
{
|
||||
return Database.Procedure(procName, args);
|
||||
}
|
||||
|
||||
/// <summary>Execute non query.</summary>
|
||||
@@ -412,13 +458,7 @@ namespace DynamORM
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Execute(string sql, params object[] args)
|
||||
{
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
return cmd
|
||||
.SetCommand(sql).AddParameters(Database, args)
|
||||
.ExecuteNonQuery();
|
||||
}
|
||||
return Database.Execute(sql, args);
|
||||
}
|
||||
|
||||
/// <summary>Execute non query.</summary>
|
||||
@@ -426,41 +466,15 @@ namespace DynamORM
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Execute(IDynamicQueryBuilder builder)
|
||||
{
|
||||
using (var con = Database.Open())
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
return cmd
|
||||
.SetCommand(builder)
|
||||
.ExecuteNonQuery();
|
||||
}
|
||||
return Database.Execute(builder);
|
||||
}
|
||||
|
||||
/// <summary>Execute non query.</summary>
|
||||
/// <param name="builers">Command builders.</param>
|
||||
/// <param name="builders">Command builders.</param>
|
||||
/// <returns>Number of affected rows.</returns>
|
||||
public virtual int Execute(IDynamicQueryBuilder[] builers)
|
||||
public virtual int Execute(IDynamicQueryBuilder[] builders)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
using (var con = Database.Open())
|
||||
{
|
||||
using (var trans = con.BeginTransaction())
|
||||
{
|
||||
foreach (var builder in builers)
|
||||
{
|
||||
using (var cmd = con.CreateCommand())
|
||||
{
|
||||
ret += cmd
|
||||
.SetCommand(builder)
|
||||
.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
trans.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return Database.Execute(builders);
|
||||
}
|
||||
|
||||
#endregion Basic Queries
|
||||
@@ -469,9 +483,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Create new <see cref="DynamicInsertQueryBuilder"/>.</summary>
|
||||
/// <returns>New <see cref="DynamicInsertQueryBuilder"/> instance.</returns>
|
||||
public DynamicInsertQueryBuilder Insert()
|
||||
public dynamic Insert()
|
||||
{
|
||||
return new DynamicInsertQueryBuilder(this);
|
||||
return new DynamicProxy<IDynamicInsertQueryBuilder>(new DynamicInsertQueryBuilder(this.Database, this.FullName));
|
||||
}
|
||||
|
||||
/// <summary>Adds a record to the database. You can pass in an Anonymous object, an <see cref="ExpandoObject"/>,
|
||||
@@ -492,9 +506,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Create new <see cref="DynamicUpdateQueryBuilder"/>.</summary>
|
||||
/// <returns>New <see cref="DynamicUpdateQueryBuilder"/> instance.</returns>
|
||||
public DynamicUpdateQueryBuilder Update()
|
||||
public dynamic Update()
|
||||
{
|
||||
return new DynamicUpdateQueryBuilder(this);
|
||||
return new DynamicProxy<IDynamicUpdateQueryBuilder>(new DynamicUpdateQueryBuilder(this.Database, this.FullName));
|
||||
}
|
||||
|
||||
/// <summary>Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
|
||||
@@ -530,9 +544,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Create new <see cref="DynamicDeleteQueryBuilder"/>.</summary>
|
||||
/// <returns>New <see cref="DynamicDeleteQueryBuilder"/> instance.</returns>
|
||||
public DynamicDeleteQueryBuilder Delete()
|
||||
public dynamic Delete()
|
||||
{
|
||||
return new DynamicDeleteQueryBuilder(this);
|
||||
return new DynamicProxy<IDynamicDeleteQueryBuilder>(new DynamicDeleteQueryBuilder(this.Database, this.FullName));
|
||||
}
|
||||
|
||||
/// <summary>Removes a record from the database. You can pass in an Anonymous object, an <see cref="ExpandoObject"/>,
|
||||
@@ -561,16 +575,16 @@ namespace DynamORM
|
||||
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
|
||||
{
|
||||
// parse the method
|
||||
var info = binder.CallInfo;
|
||||
CallInfo info = binder.CallInfo;
|
||||
|
||||
// Get generic types
|
||||
var types = binder.GetGenericTypeArguments();
|
||||
IList<Type> types = binder.GetGenericTypeArguments();
|
||||
|
||||
// accepting named args only... SKEET!
|
||||
if (info.ArgumentNames.Count != args.Length)
|
||||
throw new InvalidOperationException("Please use named arguments for this type of query - the column name, orderby, columns, etc");
|
||||
|
||||
var op = binder.Name;
|
||||
string op = binder.Name;
|
||||
|
||||
// Avoid strange things
|
||||
if (!_allowedCommands.Contains(op))
|
||||
@@ -600,17 +614,21 @@ namespace DynamORM
|
||||
|
||||
private object DynamicInsert(object[] args, CallInfo info, IList<Type> types)
|
||||
{
|
||||
var builder = new DynamicInsertQueryBuilder(this);
|
||||
DynamicInsertQueryBuilder builder = new DynamicInsertQueryBuilder(this.Database);
|
||||
|
||||
if (types != null && types.Count == 1)
|
||||
HandleTypeArgument<DynamicInsertQueryBuilder>(null, info, ref types, builder, 0);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
|
||||
builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema);
|
||||
|
||||
// loop the named args - see if we have order, columns and constraints
|
||||
if (info.ArgumentNames.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
var name = info.ArgumentNames[i].ToLower();
|
||||
string fullName = info.ArgumentNames[i];
|
||||
string name = fullName.ToLower();
|
||||
|
||||
switch (name)
|
||||
{
|
||||
@@ -631,7 +649,7 @@ namespace DynamORM
|
||||
break;
|
||||
|
||||
default:
|
||||
builder.Insert(name, args[i]);
|
||||
builder.Insert(fullName, args[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -643,17 +661,21 @@ namespace DynamORM
|
||||
|
||||
private object DynamicUpdate(object[] args, CallInfo info, IList<Type> types)
|
||||
{
|
||||
var builder = new DynamicUpdateQueryBuilder(this);
|
||||
DynamicUpdateQueryBuilder builder = new DynamicUpdateQueryBuilder(this.Database);
|
||||
|
||||
if (types != null && types.Count == 1)
|
||||
HandleTypeArgument<DynamicUpdateQueryBuilder>(null, info, ref types, builder, 0);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
|
||||
builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema);
|
||||
|
||||
// loop the named args - see if we have order, columns and constraints
|
||||
if (info.ArgumentNames.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
var name = info.ArgumentNames[i].ToLower();
|
||||
string fullName = info.ArgumentNames[i];
|
||||
string name = fullName.ToLower();
|
||||
|
||||
switch (name)
|
||||
{
|
||||
@@ -682,7 +704,7 @@ namespace DynamORM
|
||||
break;
|
||||
|
||||
default:
|
||||
builder.Update(name, args[i]);
|
||||
builder.Update(fullName, args[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -694,17 +716,21 @@ namespace DynamORM
|
||||
|
||||
private object DynamicDelete(object[] args, CallInfo info, IList<Type> types)
|
||||
{
|
||||
var builder = new DynamicDeleteQueryBuilder(this);
|
||||
DynamicDeleteQueryBuilder builder = new DynamicDeleteQueryBuilder(this.Database);
|
||||
|
||||
if (types != null && types.Count == 1)
|
||||
HandleTypeArgument<DynamicDeleteQueryBuilder>(null, info, ref types, builder, 0);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
|
||||
builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema);
|
||||
|
||||
// loop the named args - see if we have order, columns and constraints
|
||||
if (info.ArgumentNames.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
var name = info.ArgumentNames[i].ToLower();
|
||||
string fullName = info.ArgumentNames[i];
|
||||
string name = fullName.ToLower();
|
||||
|
||||
switch (name)
|
||||
{
|
||||
@@ -729,7 +755,7 @@ namespace DynamORM
|
||||
break;
|
||||
|
||||
default:
|
||||
builder.Where(name, args[i]);
|
||||
builder.Where(fullName, args[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -742,55 +768,76 @@ namespace DynamORM
|
||||
private object DynamicQuery(object[] args, CallInfo info, string op, IList<Type> types)
|
||||
{
|
||||
object result;
|
||||
var builder = new DynamicSelectQueryBuilder(this);
|
||||
DynamicSelectQueryBuilder builder = new DynamicSelectQueryBuilder(this.Database);
|
||||
|
||||
if (types != null && types.Count == 1)
|
||||
HandleTypeArgument<DynamicSelectQueryBuilder>(null, info, ref types, builder, 0);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
|
||||
builder.From(x => string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName));
|
||||
|
||||
// loop the named args - see if we have order, columns and constraints
|
||||
if (info.ArgumentNames.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
var name = info.ArgumentNames[i].ToLower();
|
||||
string fullName = info.ArgumentNames[i];
|
||||
string name = fullName.ToLower();
|
||||
|
||||
// TODO: Make this nicer
|
||||
switch (name)
|
||||
{
|
||||
case "order":
|
||||
if (args[i] is string)
|
||||
builder.OrderBy(((string)args[i]).Split(','));
|
||||
builder.OrderByColumn(((string)args[i]).Split(','));
|
||||
else if (args[i] is string[])
|
||||
builder.OrderBy(args[i] as string);
|
||||
builder.OrderByColumn(args[i] as string);
|
||||
else if (args[i] is DynamicColumn[])
|
||||
builder.OrderBy((DynamicColumn[])args[i]);
|
||||
builder.OrderByColumn((DynamicColumn[])args[i]);
|
||||
else if (args[i] is DynamicColumn)
|
||||
builder.OrderBy((DynamicColumn)args[i]);
|
||||
builder.OrderByColumn((DynamicColumn)args[i]);
|
||||
else goto default;
|
||||
break;
|
||||
|
||||
case "group":
|
||||
if (args[i] is string)
|
||||
builder.GroupBy(((string)args[i]).Split(','));
|
||||
builder.GroupByColumn(((string)args[i]).Split(','));
|
||||
else if (args[i] is string[])
|
||||
builder.GroupBy(args[i] as string);
|
||||
builder.GroupByColumn(args[i] as string);
|
||||
else if (args[i] is DynamicColumn[])
|
||||
builder.GroupBy((DynamicColumn[])args[i]);
|
||||
builder.GroupByColumn((DynamicColumn[])args[i]);
|
||||
else if (args[i] is DynamicColumn)
|
||||
builder.GroupBy((DynamicColumn)args[i]);
|
||||
builder.GroupByColumn((DynamicColumn)args[i]);
|
||||
else goto default;
|
||||
break;
|
||||
|
||||
case "columns":
|
||||
if (args[i] is string)
|
||||
builder.Select(((string)args[i]).Split(','));
|
||||
else if (args[i] is string[])
|
||||
builder.Select(args[i] as string);
|
||||
else if (args[i] is DynamicColumn[])
|
||||
builder.Select((DynamicColumn[])args[i]);
|
||||
else if (args[i] is DynamicColumn)
|
||||
builder.Select((DynamicColumn)args[i]);
|
||||
else goto default;
|
||||
{
|
||||
string agregate = (op == "Sum" || op == "Max" || op == "Min" || op == "Avg" || op == "Count") ?
|
||||
op.ToUpper() : null;
|
||||
|
||||
if (args[i] is string || args[i] is string[])
|
||||
builder.SelectColumn((args[i] as String).NullOr(s => s.Split(','), args[i] as String[])
|
||||
.Select(c =>
|
||||
{
|
||||
DynamicColumn col = DynamicColumn.ParseSelectColumn(c);
|
||||
if (string.IsNullOrEmpty(col.Aggregate))
|
||||
col.Aggregate = agregate;
|
||||
|
||||
return col;
|
||||
}).ToArray());
|
||||
else if (args[i] is DynamicColumn || args[i] is DynamicColumn[])
|
||||
builder.SelectColumn((args[i] as DynamicColumn).NullOr(c => new DynamicColumn[] { c }, args[i] as DynamicColumn[])
|
||||
.Select(c =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(c.Aggregate))
|
||||
c.Aggregate = agregate;
|
||||
|
||||
return c;
|
||||
}).ToArray());
|
||||
else goto default;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "where":
|
||||
@@ -799,7 +846,7 @@ namespace DynamORM
|
||||
|
||||
case "table":
|
||||
if (args[i] is string)
|
||||
builder.Table(args[i].ToString());
|
||||
builder.From(x => args[i].ToString());
|
||||
else goto default;
|
||||
break;
|
||||
|
||||
@@ -810,20 +857,15 @@ namespace DynamORM
|
||||
break;
|
||||
|
||||
default:
|
||||
builder.Where(name, args[i]);
|
||||
builder.Where(fullName, args[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "Count" && builder.Columns.Count == 0)
|
||||
if (op == "Count" && !builder.HasSelectColumns)
|
||||
{
|
||||
result = Scalar(builder.Select(new DynamicColumn
|
||||
{
|
||||
ColumnName = "*",
|
||||
Aggregate = op.ToUpper(),
|
||||
Alias = "Count"
|
||||
}));
|
||||
result = Scalar(builder.Select(x => x.Count()));
|
||||
|
||||
if (result is long)
|
||||
result = (int)(long)result;
|
||||
@@ -831,41 +873,23 @@ namespace DynamORM
|
||||
else if (op == "Sum" || op == "Max" ||
|
||||
op == "Min" || op == "Avg" || op == "Count")
|
||||
{
|
||||
if (builder.Columns.Count == 0)
|
||||
throw new InvalidOperationException("You must select at least one column to agregate.");
|
||||
if (!builder.HasSelectColumns)
|
||||
throw new InvalidOperationException("You must select one column to agregate.");
|
||||
|
||||
foreach (var o in builder.Columns)
|
||||
o.Aggregate = op.ToUpper();
|
||||
result = Scalar(builder);
|
||||
|
||||
if (builder.Columns.Count == 1)
|
||||
{
|
||||
result = Scalar(builder);
|
||||
|
||||
if (op == "Count" && result is long)
|
||||
result = (int)(long)result;
|
||||
else if (result == DBNull.Value)
|
||||
result = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Query(builder).FirstOrDefault(); // return lots
|
||||
}
|
||||
if (op == "Count" && result is long)
|
||||
result = (int)(long)result;
|
||||
else if (result == DBNull.Value)
|
||||
result = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// build the SQL
|
||||
var justOne = op == "First" || op == "Last" || op == "Get" || op == "Single";
|
||||
bool justOne = op == "First" || op == "Last" || op == "Get" || op == "Single";
|
||||
|
||||
// Be sure to sort by DESC on selected columns
|
||||
if (op == "Last")
|
||||
{
|
||||
if (builder.Order.Count > 0)
|
||||
foreach (var o in builder.Order)
|
||||
o.Order = o.Order == DynamicColumn.SortOrder.Desc ?
|
||||
DynamicColumn.SortOrder.Asc : DynamicColumn.SortOrder.Desc;
|
||||
}
|
||||
|
||||
if (justOne && !(op == "Last" && builder.Order.Count == 0))
|
||||
if (justOne && !(op == "Last"))
|
||||
{
|
||||
if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset)
|
||||
builder.Limit(1);
|
||||
@@ -875,7 +899,7 @@ namespace DynamORM
|
||||
|
||||
if (op == "Scalar")
|
||||
{
|
||||
if (builder.Columns.Count != 1)
|
||||
if (!builder.HasSelectColumns)
|
||||
throw new InvalidOperationException("You must select one column in scalar statement.");
|
||||
|
||||
result = Scalar(builder);
|
||||
@@ -884,7 +908,7 @@ namespace DynamORM
|
||||
{
|
||||
if (justOne)
|
||||
{
|
||||
if (op == "Last" && builder.Order.Count == 0)
|
||||
if (op == "Last")
|
||||
result = Query(builder).LastOrDefault(); // Last record fallback
|
||||
else
|
||||
result = Query(builder).FirstOrDefault(); // return a single record
|
||||
@@ -908,7 +932,7 @@ namespace DynamORM
|
||||
return result;
|
||||
}
|
||||
|
||||
private void HandleTypeArgument<T>(object[] args, CallInfo info, ref IList<Type> types, DynamicQueryBuilder<T> builder, int i) where T : class
|
||||
private void HandleTypeArgument<T>(object[] args, CallInfo info, ref IList<Type> types, T builder, int i) where T : DynamicQueryBuilder
|
||||
{
|
||||
if (args != null)
|
||||
{
|
||||
@@ -924,7 +948,7 @@ namespace DynamORM
|
||||
|
||||
#endregion Universal Dynamic Invoker
|
||||
|
||||
#region IDisposable Members
|
||||
#region IExtendedDisposable Members
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
@@ -936,9 +960,14 @@ namespace DynamORM
|
||||
Database.RemoveFromCache(this);
|
||||
Database = null;
|
||||
}
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
#endregion IDisposable Members
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
#endregion IExtendedDisposable Members
|
||||
|
||||
#region ICloneable Members
|
||||
|
||||
@@ -958,4 +987,6 @@ namespace DynamORM
|
||||
|
||||
#endregion ICloneable Members
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,18 +27,23 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using DynamORM.Helpers;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Helper class to easy manage transaction.</summary>
|
||||
public class DynamicTransaction : IDbTransaction, IDisposable
|
||||
public class DynamicTransaction : IDbTransaction, IExtendedDisposable
|
||||
{
|
||||
private DynamicDatabase _db;
|
||||
private DynamicConnection _con;
|
||||
private bool _singleTransaction;
|
||||
private Action _disposed;
|
||||
private bool _operational = false;
|
||||
private bool _isDisposed = false;
|
||||
private bool _isOperational = false;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicTransaction" /> class.</summary>
|
||||
/// <param name="db">Database connection manager.</param>
|
||||
@@ -46,7 +51,8 @@ namespace DynamORM
|
||||
/// <param name="singleTransaction">Are we using single transaction mode? I so... act correctly.</param>
|
||||
/// <param name="il">One of the <see cref="System.Data.IsolationLevel"/> values.</param>
|
||||
/// <param name="disposed">This action is invoked when transaction is disposed.</param>
|
||||
internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed)
|
||||
/// <param name="customParams">Pass custom transaction parameters.</param>
|
||||
internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed, object customParams)
|
||||
{
|
||||
_db = db;
|
||||
_con = con;
|
||||
@@ -58,12 +64,39 @@ namespace DynamORM
|
||||
if (!_db.TransactionPool.ContainsKey(_con.Connection))
|
||||
throw new InvalidOperationException("Can't create transaction using disposed connection.");
|
||||
else if (_singleTransaction && _db.TransactionPool[_con.Connection].Count > 0)
|
||||
_operational = false;
|
||||
{
|
||||
_isOperational = false;
|
||||
|
||||
if (_db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "BEGIN TRAN [NON OPERATIONAL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
_db.TransactionPool[_con.Connection].Push(_con.Connection.BeginTransaction());
|
||||
if (customParams != null)
|
||||
{
|
||||
MethodInfo mi = _con.Connection.GetType().GetMethods().Where(m => m.GetParameters().Count() == 1 && m.GetParameters().First().ParameterType == customParams.GetType()).FirstOrDefault();
|
||||
if (mi != null)
|
||||
{
|
||||
_db.TransactionPool[_con.Connection].Push((IDbTransaction)mi.Invoke(_con.Connection, new object[] { customParams, }));
|
||||
|
||||
if (_db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "BEGIN TRAN [CUSTOM ARGS]");
|
||||
}
|
||||
else
|
||||
throw new MissingMethodException(string.Format("Method 'BeginTransaction' accepting parameter of type '{0}' in '{1}' not found.",
|
||||
customParams.GetType().FullName, _con.Connection.GetType().FullName));
|
||||
}
|
||||
else
|
||||
{
|
||||
_db.TransactionPool[_con.Connection]
|
||||
.Push(il.HasValue ? _con.Connection.BeginTransaction(il.Value) : _con.Connection.BeginTransaction());
|
||||
|
||||
if (_db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "BEGIN TRAN");
|
||||
}
|
||||
|
||||
_db.PoolStamp = DateTime.Now.Ticks;
|
||||
_operational = true;
|
||||
_isOperational = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,9 +106,9 @@ namespace DynamORM
|
||||
{
|
||||
lock (_db.SyncLock)
|
||||
{
|
||||
if (_operational)
|
||||
if (_isOperational)
|
||||
{
|
||||
var t = _db.TransactionPool.TryGetValue(_con.Connection);
|
||||
Stack<IDbTransaction> t = _db.TransactionPool.TryGetValue(_con.Connection);
|
||||
|
||||
if (t != null && t.Count > 0)
|
||||
{
|
||||
@@ -85,10 +118,15 @@ namespace DynamORM
|
||||
|
||||
trans.Commit();
|
||||
trans.Dispose();
|
||||
|
||||
if (_db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "COMMIT");
|
||||
}
|
||||
|
||||
_operational = false;
|
||||
_isOperational = false;
|
||||
}
|
||||
else if (!_isDisposed && _db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "COMMIT [NON OPERATIONAL]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +135,9 @@ namespace DynamORM
|
||||
{
|
||||
lock (_db.SyncLock)
|
||||
{
|
||||
if (_operational)
|
||||
if (_isOperational)
|
||||
{
|
||||
var t = _db.TransactionPool.TryGetValue(_con.Connection);
|
||||
Stack<IDbTransaction> t = _db.TransactionPool.TryGetValue(_con.Connection);
|
||||
|
||||
if (t != null && t.Count > 0)
|
||||
{
|
||||
@@ -109,10 +147,15 @@ namespace DynamORM
|
||||
|
||||
trans.Rollback();
|
||||
trans.Dispose();
|
||||
|
||||
if (_db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "ROLLBACK");
|
||||
}
|
||||
|
||||
_operational = false;
|
||||
_isOperational = false;
|
||||
}
|
||||
else if (!_isDisposed && _db.DumpCommands && _db.DumpCommandDelegate != null)
|
||||
_db.DumpCommandDelegate(null, "ROLLBACK [NON OPERATIONAL]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,14 +168,22 @@ namespace DynamORM
|
||||
/// <summary>Gets <see cref="System.Data.IsolationLevel"/> for this transaction.</summary>
|
||||
public IsolationLevel IsolationLevel { get; private set; }
|
||||
|
||||
#region IExtendedDisposable Members
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_isDisposed = true;
|
||||
Rollback();
|
||||
|
||||
if (_disposed != null)
|
||||
_disposed();
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
|
||||
public bool IsDisposed { get { return !_isOperational; } }
|
||||
|
||||
#endregion IExtendedDisposable Members
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -91,13 +91,13 @@ namespace DynamORM.Helpers
|
||||
int firstCount;
|
||||
int secondCount;
|
||||
|
||||
var firstElementCounts = GetElementCounts(first, out firstCount);
|
||||
var secondElementCounts = GetElementCounts(second, out secondCount);
|
||||
Dictionary<T, int> firstElementCounts = GetElementCounts(first, out firstCount);
|
||||
Dictionary<T, int> secondElementCounts = GetElementCounts(second, out secondCount);
|
||||
|
||||
if (firstCount != secondCount)
|
||||
return true;
|
||||
|
||||
foreach (var kvp in firstElementCounts)
|
||||
foreach (KeyValuePair<T, int> kvp in firstElementCounts)
|
||||
if (kvp.Value != (secondElementCounts.TryGetNullable(kvp.Key) ?? 0))
|
||||
return true;
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace DynamORM.Helpers
|
||||
|
||||
private static Dictionary<T, int> GetElementCounts(IEnumerable<T> enumerable, out int nullCount)
|
||||
{
|
||||
var dictionary = new Dictionary<T, int>();
|
||||
Dictionary<T, int> dictionary = new Dictionary<T, int>();
|
||||
nullCount = 0;
|
||||
|
||||
foreach (T element in enumerable)
|
||||
|
||||
44
DynamORM/Helpers/DataReaderExtensions.cs
Normal file
44
DynamORM/Helpers/DataReaderExtensions.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
|
||||
namespace DynamORM.Helpers
|
||||
{
|
||||
/// <summary>Extensions for data reader handling.</summary>
|
||||
public static class DataReaderExtensions
|
||||
{
|
||||
/// <summary>Gets the data table from data reader.</summary>
|
||||
/// <param name="r">The data reader.</param>
|
||||
/// <param name="name">The name to give the table. If tableName is null or an empty string, a default name is given when added to the System.Data.DataTableCollection.</param>
|
||||
/// <param name="nameSpace">The namespace for the XML representation of the data stored in the DataTable.</param>
|
||||
/// <returns></returns>
|
||||
public static DataTable ToDataTable(this IDataReader r, string name = null, string nameSpace = null)
|
||||
{
|
||||
DataTable schemaTable = r.GetSchemaTable();
|
||||
DataTable resultTable = new DataTable(name, nameSpace);
|
||||
|
||||
foreach (DataRow col in schemaTable.Rows)
|
||||
{
|
||||
dynamic c = col.RowToDynamicUpper();
|
||||
|
||||
DataColumn dataColumn = new DataColumn();
|
||||
dataColumn.ColumnName = c.COLUMNNAME;
|
||||
dataColumn.DataType = (Type)c.DATATYPE;
|
||||
dataColumn.ReadOnly = true;
|
||||
dataColumn.Unique = c.ISUNIQUE;
|
||||
|
||||
resultTable.Columns.Add(dataColumn);
|
||||
}
|
||||
|
||||
while (r.Read())
|
||||
{
|
||||
DataRow row = resultTable.NewRow();
|
||||
for (int i = 0; i < resultTable.Columns.Count; i++)
|
||||
row[i] = r[i];
|
||||
|
||||
resultTable.Rows.Add(row);
|
||||
}
|
||||
|
||||
return resultTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
1489
DynamORM/Helpers/Dynamics/DynamicParser.cs
Normal file
1489
DynamORM/Helpers/Dynamics/DynamicParser.cs
Normal file
File diff suppressed because it is too large
Load Diff
334
DynamORM/Helpers/Dynamics/DynamicProxy.cs
Normal file
334
DynamORM/Helpers/Dynamics/DynamicProxy.cs
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Helpers.Dynamics
|
||||
{
|
||||
/// <summary>Class that allows to use interfaces as dynamic objects.</summary>
|
||||
/// <typeparam name="T">Type of class to proxy.</typeparam>
|
||||
/// <remarks>This is temporary solution. Which allows to use builders as a dynamic type.</remarks>
|
||||
public class DynamicProxy<T> : DynamicObject, IDisposable
|
||||
{
|
||||
private T _proxy;
|
||||
private Type _type;
|
||||
private Dictionary<string, DynamicPropertyInvoker> _properties;
|
||||
private Dictionary<MethodInfo, Delegate> _methods;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicProxy{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="proxiedObject">The object to which proxy should be created.</param>
|
||||
/// <exception cref="System.ArgumentNullException">The object to which proxy should be created is null.</exception>
|
||||
public DynamicProxy(T proxiedObject)
|
||||
{
|
||||
if (proxiedObject == null)
|
||||
throw new ArgumentNullException("proxiedObject");
|
||||
|
||||
_proxy = proxiedObject;
|
||||
_type = typeof(T);
|
||||
|
||||
DynamicTypeMap mapper = Mapper.DynamicMapperCache.GetMapper<T>();
|
||||
|
||||
_properties = mapper
|
||||
.ColumnsMap
|
||||
.ToDictionary(
|
||||
k => k.Value.Name,
|
||||
v => v.Value);
|
||||
|
||||
_methods = GetAllMembers(_type)
|
||||
.Where(x => x is MethodInfo)
|
||||
.Cast<MethodInfo>()
|
||||
.Where(m => !((m.Name.StartsWith("set_") && m.ReturnType == typeof(void)) || m.Name.StartsWith("get_")))
|
||||
.Where(m => !m.IsStatic && !m.IsGenericMethod)
|
||||
.ToDictionary(
|
||||
k => k,
|
||||
v =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Type type = v.ReturnType == typeof(void) ?
|
||||
Expression.GetActionType(v.GetParameters().Select(t => t.ParameterType).ToArray()) :
|
||||
Expression.GetDelegateType(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReturnType }).ToArray());
|
||||
|
||||
return Delegate.CreateDelegate(type, _proxy, v.Name);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Provides implementation for type conversion operations.
|
||||
/// Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class can override this method to specify dynamic behavior for
|
||||
/// operations that convert an object from one type to another.</summary>
|
||||
/// <param name="binder">Provides information about the conversion operation.
|
||||
/// The binder.Type property provides the type to which the object must be
|
||||
/// converted. For example, for the statement (String)sampleObject in C#
|
||||
/// (CType(sampleObject, Type) in Visual Basic), where sampleObject is an
|
||||
/// instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class, binder.Type returns the <see cref="T:System.String" /> type.
|
||||
/// The binder.Explicit property provides information about the kind of
|
||||
/// conversion that occurs. It returns true for explicit conversion and
|
||||
/// false for implicit conversion.</param>
|
||||
/// <param name="result">The result of the type conversion operation.</param>
|
||||
/// <returns>Returns <c>true</c> if the operation is successful; otherwise, <c>false</c>.
|
||||
/// If this method returns false, the run-time binder of the language determines the
|
||||
/// behavior. (In most cases, a language-specific run-time exception is thrown).</returns>
|
||||
public override bool TryConvert(ConvertBinder binder, out object result)
|
||||
{
|
||||
if (binder.Type == typeof(T))
|
||||
{
|
||||
result = _proxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_proxy != null &&
|
||||
binder.Type.IsAssignableFrom(_proxy.GetType()))
|
||||
{
|
||||
result = _proxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.TryConvert(binder, out result);
|
||||
}
|
||||
|
||||
/// <summary>Provides the implementation for operations that get member
|
||||
/// values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class can override this method to specify dynamic behavior for
|
||||
/// operations such as getting a value for a property.</summary>
|
||||
/// <param name="binder">Provides information about the object that
|
||||
/// called the dynamic operation. The binder.Name property provides
|
||||
/// the name of the member on which the dynamic operation is performed.
|
||||
/// For example, for the Console.WriteLine(sampleObject.SampleProperty)
|
||||
/// statement, where sampleObject is an instance of the class derived
|
||||
/// from the <see cref="T:System.Dynamic.DynamicObject" /> class,
|
||||
/// binder.Name returns "SampleProperty". The binder.IgnoreCase property
|
||||
/// specifies whether the member name is case-sensitive.</param>
|
||||
/// <param name="result">The result of the get operation. For example,
|
||||
/// if the method is called for a property, you can assign the property
|
||||
/// value to <paramref name="result" />.</param>
|
||||
/// <returns>Returns <c>true</c> if the operation is successful; otherwise,
|
||||
/// <c>false</c>. If this method returns false, the run-time binder of the
|
||||
/// language determines the behavior. (In most cases, a run-time exception
|
||||
/// is thrown).</returns>
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
try
|
||||
{
|
||||
DynamicPropertyInvoker prop = _properties.TryGetValue(binder.Name);
|
||||
|
||||
result = prop.NullOr(p => p.Get.NullOr(g => g(_proxy), null), null);
|
||||
|
||||
return prop != null && prop.Get != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Cannot get member {0}", binder.Name), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Provides the implementation for operations that set member
|
||||
/// values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class can override this method to specify dynamic behavior for operations
|
||||
/// such as setting a value for a property.</summary>
|
||||
/// <param name="binder">Provides information about the object that called
|
||||
/// the dynamic operation. The binder.Name property provides the name of
|
||||
/// the member to which the value is being assigned. For example, for the
|
||||
/// statement sampleObject.SampleProperty = "Test", where sampleObject is
|
||||
/// an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class, binder.Name returns "SampleProperty". The binder.IgnoreCase
|
||||
/// property specifies whether the member name is case-sensitive.</param>
|
||||
/// <param name="value">The value to set to the member. For example, for
|
||||
/// sampleObject.SampleProperty = "Test", where sampleObject is an instance
|
||||
/// of the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class, the <paramref name="value" /> is "Test".</param>
|
||||
/// <returns>Returns <c>true</c> if the operation is successful; otherwise,
|
||||
/// <c>false</c>. If this method returns false, the run-time binder of the
|
||||
/// language determines the behavior. (In most cases, a language-specific
|
||||
/// run-time exception is thrown).</returns>
|
||||
public override bool TrySetMember(SetMemberBinder binder, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
DynamicPropertyInvoker prop = _properties.TryGetValue(binder.Name);
|
||||
|
||||
if (prop != null && prop.Setter != null)
|
||||
{
|
||||
prop.Set(_proxy, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Cannot set member {0} to '{1}'", binder.Name, value), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Provides the implementation for operations that invoke a member.
|
||||
/// Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class can override this method to specify dynamic behavior for
|
||||
/// operations such as calling a method.</summary>
|
||||
/// <param name="binder">Provides information about the dynamic operation.
|
||||
/// The binder.Name property provides the name of the member on which the
|
||||
/// dynamic operation is performed. For example, for the statement
|
||||
/// sampleObject.SampleMethod(100), where sampleObject is an instance of
|
||||
/// the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||
/// class, binder.Name returns "SampleMethod". The binder.IgnoreCase property
|
||||
/// specifies whether the member name is case-sensitive.</param>
|
||||
/// <param name="args">The arguments that are passed to the object member
|
||||
/// during the invoke operation. For example, for the statement
|
||||
/// sampleObject.SampleMethod(100), where sampleObject is derived from the
|
||||
/// <see cref="T:System.Dynamic.DynamicObject" /> class,
|
||||
/// First element of <paramref name="args" /> is equal to 100.</param>
|
||||
/// <param name="result">The result of the member invocation.</param>
|
||||
/// <returns>Returns <c>true</c> if the operation is successful; otherwise,
|
||||
/// <c>false</c>. If this method returns false, the run-time binder of the
|
||||
/// language determines the behavior. (In most cases, a language-specific
|
||||
/// run-time exception is thrown).</returns>
|
||||
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
|
||||
{
|
||||
return TryInvokeMethod(binder.Name, out result, args) || base.TryInvokeMember(binder, args, out result);
|
||||
}
|
||||
|
||||
private bool TryInvokeMethod(string name, out object result, object[] args)
|
||||
{
|
||||
result = null;
|
||||
|
||||
MethodInfo mi = _methods.Keys
|
||||
.Where(m => m.Name == name)
|
||||
.FirstOrDefault(m =>
|
||||
CompareTypes(m.GetParameters().ToArray(),
|
||||
args.Select(a => a.GetType()).ToArray()));
|
||||
|
||||
Delegate d = _methods.TryGetValue(mi);
|
||||
|
||||
if (d != null)
|
||||
{
|
||||
result = d.DynamicInvoke(CompleteArguments(mi.GetParameters().ToArray(), args));
|
||||
|
||||
if (d.Method.ReturnType == _type && result is T)
|
||||
result = new DynamicProxy<T>((T)result);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (mi != null)
|
||||
{
|
||||
result = mi.Invoke(_proxy, CompleteArguments(mi.GetParameters().ToArray(), args));
|
||||
|
||||
if (mi.ReturnType == _type && result is T)
|
||||
result = new DynamicProxy<T>((T)result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CompareTypes(ParameterInfo[] parameters, Type[] types)
|
||||
{
|
||||
if (parameters.Length < types.Length || parameters.Count(p => !p.IsOptional) > types.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
if (types[i] != parameters[i].ParameterType && !parameters[i].ParameterType.IsAssignableFrom(types[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private object[] CompleteArguments(ParameterInfo[] parameters, object[] arguments)
|
||||
{
|
||||
return arguments.Concat(parameters.Skip(arguments.Length).Select(p => p.DefaultValue)).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<MemberInfo> GetAllMembers(Type type)
|
||||
{
|
||||
if (type.IsInterface)
|
||||
{
|
||||
List<MemberInfo> members = new List<MemberInfo>();
|
||||
List<Type> considered = new List<Type>();
|
||||
Queue<Type> queue = new Queue<Type>();
|
||||
|
||||
considered.Add(type);
|
||||
queue.Enqueue(type);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
Type subType = queue.Dequeue();
|
||||
foreach (Type subInterface in subType.GetInterfaces())
|
||||
{
|
||||
if (considered.Contains(subInterface)) continue;
|
||||
|
||||
considered.Add(subInterface);
|
||||
queue.Enqueue(subInterface);
|
||||
}
|
||||
|
||||
MemberInfo[] typeProperties = subType.GetMembers(
|
||||
BindingFlags.FlattenHierarchy
|
||||
| BindingFlags.Public
|
||||
| BindingFlags.Instance);
|
||||
|
||||
IEnumerable<MemberInfo> newPropertyInfos = typeProperties
|
||||
.Where(x => !members.Contains(x));
|
||||
|
||||
members.InsertRange(0, newPropertyInfos);
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
return type.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
|
||||
}
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
object res;
|
||||
TryInvokeMethod("Dispose", out res, new object[] { });
|
||||
|
||||
_properties.Clear();
|
||||
|
||||
_methods = null;
|
||||
_properties = null;
|
||||
_type = null;
|
||||
_proxy = default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -61,31 +61,48 @@ namespace DynamORM.Helpers
|
||||
// HACK: Creating binders assuming types are correct... this may fail.
|
||||
if (IsMono)
|
||||
{
|
||||
var binderType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder");
|
||||
Type binderType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder");
|
||||
|
||||
if (binderType != null)
|
||||
{
|
||||
ParameterExpression param = Expression.Parameter(typeof(InvokeMemberBinder), "o");
|
||||
|
||||
try
|
||||
{
|
||||
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Field(
|
||||
Expression.TypeAs(param, binderType), "typeArguments"),
|
||||
typeof(IList<Type>)), param).Compile();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
PropertyInfo prop = binderType.GetProperty("TypeArguments");
|
||||
|
||||
if (!prop.CanRead)
|
||||
return null;
|
||||
|
||||
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Field(
|
||||
Expression.TypeAs(param, binderType), "typeArguments"),
|
||||
Expression.Property(
|
||||
Expression.TypeAs(param, binderType), prop.Name),
|
||||
typeof(IList<Type>)), param).Compile();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var inter = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
|
||||
Type inter = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
|
||||
|
||||
if (inter != null)
|
||||
{
|
||||
var prop = inter.GetProperty("TypeArguments");
|
||||
PropertyInfo prop = inter.GetProperty("TypeArguments");
|
||||
|
||||
if (!prop.CanRead)
|
||||
return null;
|
||||
|
||||
var objParm = Expression.Parameter(typeof(InvokeMemberBinder), "o");
|
||||
ParameterExpression objParm = Expression.Parameter(typeof(InvokeMemberBinder), "o");
|
||||
|
||||
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
|
||||
Expression.TypeAs(
|
||||
@@ -121,12 +138,20 @@ namespace DynamORM.Helpers
|
||||
// In mono this is trivial.
|
||||
|
||||
// First we get field info.
|
||||
var field = binder.GetType().GetField("typeArguments", BindingFlags.Instance |
|
||||
FieldInfo field = binder.GetType().GetField("typeArguments", BindingFlags.Instance |
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
// If this was a success get and return it's value
|
||||
if (field != null)
|
||||
return field.GetValue(binder) as IList<Type>;
|
||||
else
|
||||
{
|
||||
PropertyInfo prop = binder.GetType().GetProperty("TypeArguments");
|
||||
|
||||
// If we have a property, return it's value
|
||||
if (prop != null)
|
||||
return prop.GetValue(binder, null) as IList<Type>;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -134,12 +159,12 @@ namespace DynamORM.Helpers
|
||||
// In this case, we need more aerobic :D
|
||||
|
||||
// First, get the interface
|
||||
var inter = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
|
||||
Type inter = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
|
||||
|
||||
if (inter != null)
|
||||
{
|
||||
// Now get property.
|
||||
var prop = inter.GetProperty("TypeArguments");
|
||||
PropertyInfo prop = inter.GetProperty("TypeArguments");
|
||||
|
||||
// If we have a property, return it's value
|
||||
if (prop != null)
|
||||
|
||||
44
DynamORM/Helpers/IExtendedDisposable.cs
Normal file
44
DynamORM/Helpers/IExtendedDisposable.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace DynamORM.Helpers
|
||||
{
|
||||
/// <summary>Extends <see cref="IDisposable"/> interface.</summary>
|
||||
public interface IExtendedDisposable : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisposed { get; }
|
||||
}
|
||||
}
|
||||
39
DynamORM/Helpers/IFinalizerDisposable.cs
Normal file
39
DynamORM/Helpers/IFinalizerDisposable.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace DynamORM.Helpers
|
||||
{
|
||||
/// <summary>Extends <see cref="IExtendedDisposable"/> interface.</summary>
|
||||
public interface IFinalizerDisposable : IExtendedDisposable
|
||||
{
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
/// <param name="disposing">If set to <c>true</c> dispose object.</param>
|
||||
void Dispose(bool disposing);
|
||||
}
|
||||
}
|
||||
332
DynamORM/Helpers/StringExtensions.cs
Normal file
332
DynamORM/Helpers/StringExtensions.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace DynamORM.Helpers
|
||||
{
|
||||
/// <summary>Class containing useful string extensions.</summary>
|
||||
internal static class StringExtensions
|
||||
{
|
||||
static StringExtensions()
|
||||
{
|
||||
InvalidMultipartMemberChars = _InvalidMultipartMemberChars.ToCharArray();
|
||||
InvalidMemberChars = _InvalidMemberChars.ToCharArray();
|
||||
}
|
||||
|
||||
private static readonly string _InvalidMultipartMemberChars = " +-*/^%[]{}()!\"\\&=?¿";
|
||||
private static readonly string _InvalidMemberChars = "." + _InvalidMultipartMemberChars;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array with some invalid characters that cannot be used with multipart names for class members.
|
||||
/// </summary>
|
||||
public static char[] InvalidMultipartMemberChars { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array with some invalid characters that cannot be used with names for class members.
|
||||
/// </summary>
|
||||
public static char[] InvalidMemberChars { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides with an alternate and generic way to obtain an alternate string representation for this instance,
|
||||
/// applying the following rules:
|
||||
/// <para>- Null values are returned as with the <paramref name="nullString"/> value, or a null object.</para>
|
||||
/// <para>- Enum values are translated into their string representation.</para>
|
||||
/// <para>- If the type has override the 'ToString' method then it is used.</para>
|
||||
/// <para>- If it is a dictionary, then a collection of key/value pairs where the value part is also translated.</para>
|
||||
/// <para>- If it is a collection, then a collection of value items also translated.</para>
|
||||
/// <para>- If it has public public properties (or if not, if it has public fields), the collection of name/value
|
||||
/// pairs, with the values translated.</para>
|
||||
/// <para>- Finally it falls back to the standard 'type.FullName' mechanism.</para>
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to obtain its alternate string representation from.</param>
|
||||
/// <param name="brackets">The brackets to use if needed. If not null it must be at least a 2-chars' array containing
|
||||
/// the opening and closing brackets.</param>
|
||||
/// <param name="nullString">Representation of null string..</param>
|
||||
/// <returns>The alternate string representation of this object.</returns>
|
||||
public static string Sketch(this object obj, char[] brackets = null, string nullString = "(null)")
|
||||
{
|
||||
if (obj == null) return nullString;
|
||||
if (obj is string) return (string)obj;
|
||||
|
||||
Type type = obj.GetType();
|
||||
if (type.IsEnum) return obj.ToString();
|
||||
|
||||
// If the ToString() method has been overriden (by the type itself, or by its parents), let's use it...
|
||||
MethodInfo method = type.GetMethod("ToString", Type.EmptyTypes);
|
||||
if (method.DeclaringType != typeof(object)) return obj.ToString();
|
||||
|
||||
// For alll other cases...
|
||||
StringBuilder sb = new StringBuilder();
|
||||
bool first = true;
|
||||
|
||||
// Dictionaries...
|
||||
if (obj is IDictionary)
|
||||
{
|
||||
if (brackets == null || brackets.Length < 2)
|
||||
brackets = "[]".ToCharArray();
|
||||
|
||||
sb.AppendFormat("{0}", brackets[0]); first = true; foreach (DictionaryEntry kvp in (IDictionary)obj)
|
||||
{
|
||||
if (!first) sb.Append(", "); else first = false;
|
||||
sb.AppendFormat("'{0}'='{1}'", kvp.Key.Sketch(), kvp.Value.Sketch());
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0}", brackets[1]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// IEnumerables...
|
||||
IEnumerator ator = null;
|
||||
if (obj is IEnumerable)
|
||||
ator = ((IEnumerable)obj).GetEnumerator();
|
||||
else
|
||||
{
|
||||
method = type.GetMethod("GetEnumerator", Type.EmptyTypes);
|
||||
if (method != null)
|
||||
ator = (IEnumerator)method.Invoke(obj, null);
|
||||
}
|
||||
|
||||
if (ator != null)
|
||||
{
|
||||
if (brackets == null || brackets.Length < 2) brackets = "[]".ToCharArray();
|
||||
sb.AppendFormat("{0}", brackets[0]); first = true; while (ator.MoveNext())
|
||||
{
|
||||
if (!first) sb.Append(", "); else first = false;
|
||||
sb.AppendFormat("{0}", ator.Current.Sketch());
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0}", brackets[1]);
|
||||
|
||||
if (ator is IDisposable)
|
||||
((IDisposable)ator).Dispose();
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// As a last resort, using the public properties (or fields if needed, or type name)...
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
|
||||
PropertyInfo[] props = type.GetProperties(flags);
|
||||
FieldInfo[] infos = type.GetFields(flags);
|
||||
|
||||
if (props.Length == 0 && infos.Length == 0) sb.Append(type.FullName); // Fallback if needed
|
||||
else
|
||||
{
|
||||
if (brackets == null || brackets.Length < 2) brackets = "{}".ToCharArray();
|
||||
sb.AppendFormat("{0}", brackets[0]);
|
||||
first = true;
|
||||
|
||||
if (props.Length != 0)
|
||||
{
|
||||
foreach (PropertyInfo prop in props)
|
||||
{
|
||||
if (!first) sb.Append(", "); else first = false;
|
||||
sb.AppendFormat("{0}='{1}'", prop.Name, prop.GetValue(obj, null).Sketch());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (infos.Length != 0)
|
||||
{
|
||||
foreach (FieldInfo info in infos)
|
||||
{
|
||||
if (!first) sb.Append(", "); else first = false;
|
||||
sb.AppendFormat("{0}='{1}'", info.Name, info.GetValue(obj).Sketch());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0}", brackets[1]);
|
||||
}
|
||||
|
||||
// And returning...
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the target string contains any of the characters given.
|
||||
/// </summary>
|
||||
/// <param name="source">The target string. It cannot be null.</param>
|
||||
/// <param name="items">An array containing the characters to test. It cannot be null. If empty false is returned.</param>
|
||||
/// <returns>True if the target string contains any of the characters given, false otherwise.</returns>
|
||||
public static bool ContainsAny(this string source, char[] items)
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException("source", "Source string cannot be null.");
|
||||
if (items == null) throw new ArgumentNullException("items", "Array of characters to test cannot be null.");
|
||||
|
||||
if (items.Length == 0) return false; // No characters to validate
|
||||
int ix = source.IndexOfAny(items);
|
||||
return ix >= 0 ? true : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new validated string using the rules given.
|
||||
/// </summary>
|
||||
/// <param name="source">The source string.</param>
|
||||
/// <param name="desc">A description of the source string to build errors and exceptions if needed.</param>
|
||||
/// <param name="canbeNull">True if the returned string can be null.</param>
|
||||
/// <param name="canbeEmpty">True if the returned string can be empty.</param>
|
||||
/// <param name="trim">True to trim the returned string.</param>
|
||||
/// <param name="trimStart">True to left-trim the returned string.</param>
|
||||
/// <param name="trimEnd">True to right-trim the returned string.</param>
|
||||
/// <param name="minLen">If >= 0, the min valid length for the returned string.</param>
|
||||
/// <param name="maxLen">If >= 0, the max valid length for the returned string.</param>
|
||||
/// <param name="padLeft">If not '\0', the character to use to left-pad the returned string if needed.</param>
|
||||
/// <param name="padRight">If not '\0', the character to use to right-pad the returned string if needed.</param>
|
||||
/// <param name="invalidChars">If not null, an array containing invalid chars that must not appear in the returned
|
||||
/// string.</param>
|
||||
/// <param name="validChars">If not null, an array containing the only characters that are considered valid for the
|
||||
/// returned string.</param>
|
||||
/// <returns>A new validated string.</returns>
|
||||
public static string Validated(this string source, string desc = null,
|
||||
bool canbeNull = false, bool canbeEmpty = false,
|
||||
bool trim = true, bool trimStart = false, bool trimEnd = false,
|
||||
int minLen = -1, int maxLen = -1, char padLeft = '\0', char padRight = '\0',
|
||||
char[] invalidChars = null, char[] validChars = null)
|
||||
{
|
||||
// Assuring a valid descriptor...
|
||||
if (string.IsNullOrWhiteSpace(desc)) desc = "Source";
|
||||
|
||||
// Validating if null sources are accepted...
|
||||
if (source == null)
|
||||
{
|
||||
if (!canbeNull) throw new ArgumentNullException(desc, string.Format("{0} cannot be null.", desc));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trimming if needed...
|
||||
if (trim && !(trimStart || trimEnd)) source = source.Trim();
|
||||
else
|
||||
{
|
||||
if (trimStart) source = source.TrimStart(' ');
|
||||
if (trimEnd) source = source.TrimEnd(' ');
|
||||
}
|
||||
|
||||
// Adjusting lenght...
|
||||
if (minLen > 0)
|
||||
{
|
||||
if (padLeft != '\0') source = source.PadLeft(minLen, padLeft);
|
||||
if (padRight != '\0') source = source.PadRight(minLen, padRight);
|
||||
}
|
||||
|
||||
if (maxLen > 0)
|
||||
{
|
||||
if (padLeft != '\0') source = source.PadLeft(maxLen, padLeft);
|
||||
if (padRight != '\0') source = source.PadRight(maxLen, padRight);
|
||||
}
|
||||
|
||||
// Validating emptyness and lenghts...
|
||||
if (source.Length == 0)
|
||||
{
|
||||
if (!canbeEmpty) throw new ArgumentException(string.Format("{0} cannot be empty.", desc));
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (minLen >= 0 && source.Length < minLen) throw new ArgumentException(string.Format("Lenght of {0} '{1}' is lower than '{2}'.", desc, source, minLen));
|
||||
if (maxLen >= 0 && source.Length > maxLen) throw new ArgumentException(string.Format("Lenght of {0} '{1}' is bigger than '{2}'.", desc, source, maxLen));
|
||||
|
||||
// Checking invalid chars...
|
||||
if (invalidChars != null)
|
||||
{
|
||||
int n = source.IndexOfAny(invalidChars);
|
||||
if (n >= 0) throw new ArgumentException(string.Format("Invalid character '{0}' found in {1} '{2}'.", source[n], desc, source));
|
||||
}
|
||||
|
||||
// Checking valid chars...
|
||||
if (validChars != null)
|
||||
{
|
||||
int n = validChars.ToString().IndexOfAny(source.ToCharArray());
|
||||
if (n >= 0) throw new ArgumentException(string.Format("Invalid character '{0}' found in {1} '{2}'.", validChars.ToString()[n], desc, source));
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the given string with the 'something AS alias' format, returning a tuple containing its 'something' and 'alias' parts.
|
||||
/// If no alias is detected, then its component in the tuple returned is null and all the contents from the source
|
||||
/// string are considered as the 'something' part.
|
||||
/// </summary>
|
||||
/// <param name="source">The source string.</param>
|
||||
/// <returns>A tuple containing the 'something' and 'alias' parts.</returns>
|
||||
public static Tuple<string, string> SplitSomethingAndAlias(this string source)
|
||||
{
|
||||
source = source.Validated("[Something AS Alias]");
|
||||
|
||||
string something = null;
|
||||
string alias = null;
|
||||
int n = source.LastIndexOf(" AS ", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (n < 0)
|
||||
something = source;
|
||||
else
|
||||
{
|
||||
something = source.Substring(0, n);
|
||||
alias = source.Substring(n + 4);
|
||||
}
|
||||
|
||||
return new Tuple<string, string>(something, alias);
|
||||
}
|
||||
|
||||
/// <summary>Allows to replace parameters inside of string.</summary>
|
||||
/// <param name="stringToFill">String containing parameters in format <c>[$ParameterName]</c>.</param>
|
||||
/// <param name="getValue">Function that should return value that will be placed in string in place of placed parameter.</param>
|
||||
/// <param name="prefix">Prefix of the parameter. This value can't be null or empty, default value <code>[$</code>.</param>
|
||||
/// <param name="sufix">Suffix of the parameter. This value can't be null or empty, default value <code>]</code>.</param>
|
||||
/// <returns>Parsed string.</returns>
|
||||
public static string FillStringWithVariables(this string stringToFill, Func<string, string> getValue, string prefix = "[$", string sufix = "]")
|
||||
{
|
||||
int startPos = 0, endPos = 0;
|
||||
prefix.Validated();
|
||||
sufix.Validated();
|
||||
|
||||
startPos = stringToFill.IndexOf(prefix, startPos);
|
||||
while (startPos >= 0)
|
||||
{
|
||||
endPos = stringToFill.IndexOf(sufix, startPos + prefix.Length);
|
||||
int nextStartPos = stringToFill.IndexOf(prefix, startPos + prefix.Length);
|
||||
|
||||
if (endPos > startPos + prefix.Length + 1 && (nextStartPos > endPos || nextStartPos == -1))
|
||||
{
|
||||
string paramName = stringToFill.Substring(startPos + prefix.Length, endPos - (startPos + prefix.Length));
|
||||
|
||||
stringToFill = stringToFill
|
||||
.Remove(startPos, (endPos - startPos) + sufix.Length)
|
||||
.Insert(startPos, getValue(paramName));
|
||||
}
|
||||
|
||||
startPos = stringToFill.IndexOf(prefix, startPos + prefix.Length);
|
||||
}
|
||||
|
||||
return stringToFill;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
DynamORM/Helpers/UnclassifiedExtensions.cs
Normal file
92
DynamORM/Helpers/UnclassifiedExtensions.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DynamORM.Helpers
|
||||
{
|
||||
/// <summary>Class contains unclassified extensions.</summary>
|
||||
internal static class UnclassifiedExtensions
|
||||
{
|
||||
/// <summary>Easy way to use conditional value.</summary>
|
||||
/// <remarks>Includes <see cref="DBNull.Value"/>.</remarks>
|
||||
/// <typeparam name="T">Input object type to check.</typeparam>
|
||||
/// <typeparam name="R">Result type.</typeparam>
|
||||
/// <param name="obj">The object to check.</param>
|
||||
/// <param name="func">The select function.</param>
|
||||
/// <param name="elseValue">The else value.</param>
|
||||
/// <returns>Selected value or default value.</returns>
|
||||
/// <example>It lets you do this:
|
||||
/// <code>var lname = thingy.NullOr(t => t.Name).NullOr(n => n.ToLower());</code>
|
||||
/// which is more fluent and (IMO) easier to read than this:
|
||||
/// <code>var lname = (thingy != null ? thingy.Name : null) != null ? thingy.Name.ToLower() : null;</code>
|
||||
/// </example>
|
||||
public static R NullOr<T, R>(this T obj, Func<T, R> func, R elseValue = default(R)) where T : class
|
||||
{
|
||||
return obj != null && obj != DBNull.Value ?
|
||||
func(obj) : elseValue;
|
||||
}
|
||||
|
||||
/// <summary>Easy way to use conditional value.</summary>
|
||||
/// <remarks>Includes <see cref="DBNull.Value"/>.</remarks>
|
||||
/// <typeparam name="T">Input object type to check.</typeparam>
|
||||
/// <typeparam name="R">Result type.</typeparam>
|
||||
/// <param name="obj">The object to check.</param>
|
||||
/// <param name="func">The select function.</param>
|
||||
/// <param name="elseFunc">The else value function.</param>
|
||||
/// <returns>Selected value or default value.</returns>
|
||||
/// <example>It lets you do this:
|
||||
/// <code>var lname = thingy.NullOr(t => t.Name).NullOr(n => n.ToLower());</code>
|
||||
/// which is more fluent and (IMO) easier to read than this:
|
||||
/// <code>var lname = (thingy != null ? thingy.Name : null) != null ? thingy.Name.ToLower() : null;</code>
|
||||
/// </example>
|
||||
public static R NullOrFn<T, R>(this T obj, Func<T, R> func, Func<R> elseFunc = null) where T : class
|
||||
{
|
||||
// Old if to avoid recurency.
|
||||
return obj != null && obj != DBNull.Value ?
|
||||
func(obj) : elseFunc != null ? elseFunc() : default(R);
|
||||
}
|
||||
|
||||
#if !NET6_0_OR_GREATER
|
||||
/// <summary>Simple distinct by selector extension.</summary>
|
||||
/// <returns>The enumerator of elements distinct by specified selector.</returns>
|
||||
/// <param name="source">Source collection.</param>
|
||||
/// <param name="keySelector">Distinct key selector.</param>
|
||||
/// <typeparam name="R">The enumerable element type parameter.</typeparam>
|
||||
/// <typeparam name="T">The selector type parameter.</typeparam>
|
||||
public static IEnumerable<R> DistinctBy<R, T>(this IEnumerable<R> source, Func<R, T> keySelector)
|
||||
{
|
||||
HashSet<T> seenKeys = new HashSet<T>();
|
||||
foreach (R element in source)
|
||||
if (seenKeys.Add(keySelector(element)))
|
||||
yield return element;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -45,6 +45,10 @@ namespace DynamORM.Mapper
|
||||
/// <summary>Gets or sets a value indicating whether column is a key.</summary>
|
||||
public bool IsKey { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether column allows null or not.</summary>
|
||||
/// <remarks>Information only.</remarks>
|
||||
public bool AllowNull { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether column should have unique value.</summary>
|
||||
/// <remarks>Used when overriding schema.</remarks>
|
||||
public bool? IsUnique { get; set; }
|
||||
@@ -61,18 +65,38 @@ namespace DynamORM.Mapper
|
||||
/// <remarks>Used when overriding schema.</remarks>
|
||||
public byte? Scale { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether this column is no allowed to be inserted.</summary>
|
||||
/// <remarks>This is only a suggestion to automated mapping.</remarks>
|
||||
public bool IsNoInsert { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether this column is no allowed to be updated.</summary>
|
||||
/// <remarks>This is only a suggestion to automated mapping.</remarks>
|
||||
public bool IsNoUpdate { get; set; }
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ColumnAttribute" /> class.</summary>
|
||||
public ColumnAttribute() { }
|
||||
public ColumnAttribute()
|
||||
{
|
||||
AllowNull = true;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ColumnAttribute" /> class.</summary>
|
||||
/// <param name="name">Name of column.</param>
|
||||
public ColumnAttribute(string name)
|
||||
: this()
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ColumnAttribute" /> class.</summary>
|
||||
/// <param name="isKey">Set column as a key column.</param>
|
||||
public ColumnAttribute(bool isKey)
|
||||
: this()
|
||||
{
|
||||
IsKey = isKey;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ColumnAttribute" /> class.</summary>
|
||||
/// <param name="name">Name of column.</param>
|
||||
/// <param name="isKey">Set column as a key column.</param>
|
||||
@@ -82,6 +106,15 @@ namespace DynamORM.Mapper
|
||||
IsKey = isKey;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ColumnAttribute" /> class.</summary>
|
||||
/// <param name="isKey">Set column as a key column.</param>
|
||||
/// <param name="type">Set column type.</param>
|
||||
public ColumnAttribute(bool isKey, DbType type)
|
||||
: this(isKey)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ColumnAttribute" /> class.</summary>
|
||||
/// <param name="name">Name of column.</param>
|
||||
/// <param name="isKey">Set column as a key column.</param>
|
||||
|
||||
120
DynamORM/Mapper/DynamicCast.cs
Normal file
120
DynamORM/Mapper/DynamicCast.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DynamORM.Mapper
|
||||
{
|
||||
/// <summary>Type cast helper.</summary>
|
||||
public static class DynamicCast
|
||||
{
|
||||
/// <summary>Gets the default value.</summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>Default instance.</returns>
|
||||
public static object GetDefaultValue(this Type type)
|
||||
{
|
||||
return type.IsValueType ? TypeDefaults.GetOrAdd(type, t => Activator.CreateInstance(t)) : null;
|
||||
}
|
||||
|
||||
/// <summary>Casts the object to this type.</summary>
|
||||
/// <param name="type">The type to which cast value.</param>
|
||||
/// <param name="val">The value to cast.</param>
|
||||
/// <returns>Value casted to new type.</returns>
|
||||
public static object CastObject(this Type type, object val)
|
||||
{
|
||||
return GetConverter(type, val)(val);
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, object> TypeDefaults = new ConcurrentDictionary<Type, object>();
|
||||
private static readonly ConcurrentDictionary<Type, Func<object, object>> TypeAsCasts = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
private static readonly ConcurrentDictionary<PairOfTypes, Func<object, object>> TypeConvert = new ConcurrentDictionary<PairOfTypes, Func<object, object>>();
|
||||
private static readonly ParameterExpression ConvParameter = Expression.Parameter(typeof(object), "val");
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
private static Func<object, object> GetConverter(Type targetType, object val)
|
||||
{
|
||||
Func<object, object> fn;
|
||||
|
||||
if (!targetType.IsValueType && !val.GetType().IsValueType)
|
||||
{
|
||||
if (!TypeAsCasts.TryGetValue(targetType, out fn))
|
||||
{
|
||||
UnaryExpression instanceCast = Expression.TypeAs(ConvParameter, targetType);
|
||||
|
||||
fn = Expression.Lambda<Func<object, object>>(Expression.TypeAs(instanceCast, typeof(object)), ConvParameter).Compile();
|
||||
TypeAsCasts.AddOrUpdate(targetType, fn, (t, f) => fn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fromType = val != null ? val.GetType() : typeof(object);
|
||||
var key = new PairOfTypes(fromType, targetType);
|
||||
if (TypeConvert.TryGetValue(key, out fn))
|
||||
return fn;
|
||||
|
||||
fn = (Func<object, object>)Expression.Lambda(Expression.Convert(Expression.Convert(Expression.Convert(ConvParameter, fromType), targetType), typeof(object)), ConvParameter).Compile();
|
||||
TypeConvert.AddOrUpdate(key, fn, (t, f) => fn);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
private class PairOfTypes
|
||||
{
|
||||
private readonly Type _first;
|
||||
private readonly Type _second;
|
||||
|
||||
public PairOfTypes(Type first, Type second)
|
||||
{
|
||||
this._first = first;
|
||||
this._second = second;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (31 * _first.GetHashCode()) + _second.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
return true;
|
||||
|
||||
var other = obj as PairOfTypes;
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return _first.Equals(other._first)
|
||||
&& _second.Equals(other._second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
35
DynamORM/Mapper/DynamicMapperException.cs
Normal file
35
DynamORM/Mapper/DynamicMapperException.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace DynamORM.Mapper
|
||||
{
|
||||
/// <summary>Exception thrown when mapper fails to set or get a property.</summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
public class DynamicMapperException : Exception
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicMapperException"/> class.</summary>
|
||||
public DynamicMapperException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicMapperException"/> class.</summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public DynamicMapperException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicMapperException"/> class.</summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
|
||||
public DynamicMapperException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicMapperException"/> class.</summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
|
||||
protected DynamicMapperException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,19 +27,44 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using DynamORM.Validation;
|
||||
|
||||
namespace DynamORM.Mapper
|
||||
{
|
||||
/// <summary>Dynamic property invoker.</summary>
|
||||
public class DynamicPropertyInvoker
|
||||
{
|
||||
internal class ParameterSpec
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public DbType Type { get; set; }
|
||||
|
||||
public int Ordinal { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Gets the array type of property if main type is a form of collection.</summary>
|
||||
public Type ArrayType { get; private set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether this property is in fact a generic list.</summary>
|
||||
public bool IsGnericEnumerable { get; private set; }
|
||||
|
||||
/// <summary>Gets the type of property.</summary>
|
||||
public Type Type { get; private set; }
|
||||
|
||||
/// <summary>Gets value getter.</summary>
|
||||
public Func<object, object> Get { get; private set; }
|
||||
|
||||
/// <summary>Gets value setter.</summary>
|
||||
public Action<object, object> Set { get; private set; }
|
||||
public Action<object, object> Setter { get; private set; }
|
||||
|
||||
/// <summary>Gets the property information.</summary>
|
||||
public PropertyInfo PropertyInfo { get; private set; }
|
||||
|
||||
/// <summary>Gets name of property.</summary>
|
||||
public string Name { get; private set; }
|
||||
@@ -47,24 +72,53 @@ namespace DynamORM.Mapper
|
||||
/// <summary>Gets type column description.</summary>
|
||||
public ColumnAttribute Column { get; private set; }
|
||||
|
||||
/// <summary>Gets type list of property requirements.</summary>
|
||||
public List<RequiredAttribute> Requirements { get; private set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether this <see cref="DynamicPropertyInvoker"/> is ignored in some cases.</summary>
|
||||
public bool Ignore { get; private set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance hold data contract type.</summary>
|
||||
public bool IsDataContract { get; private set; }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicPropertyInvoker" /> class.</summary>
|
||||
/// <param name="property">Property info to be invoked in the future.</param>
|
||||
/// <param name="attr">Column attribute if exist.</param>
|
||||
public DynamicPropertyInvoker(PropertyInfo property, ColumnAttribute attr)
|
||||
{
|
||||
PropertyInfo = property;
|
||||
Name = property.Name;
|
||||
Type = property.PropertyType;
|
||||
|
||||
var ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false);
|
||||
object[] ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false);
|
||||
Requirements = property.GetCustomAttributes(typeof(RequiredAttribute), false).Cast<RequiredAttribute>().ToList();
|
||||
|
||||
Ignore = ignore != null && ignore.Length > 0;
|
||||
|
||||
IsGnericEnumerable = Type.IsGenericEnumerable();
|
||||
|
||||
ArrayType = Type.IsArray ? Type.GetElementType() :
|
||||
IsGnericEnumerable ? Type.GetGenericArguments().First() :
|
||||
Type;
|
||||
|
||||
IsDataContract = ArrayType.GetCustomAttributes(false).Any(x => x.GetType().Name == "DataContractAttribute");
|
||||
|
||||
if (ArrayType.IsArray)
|
||||
throw new InvalidOperationException("Jagged arrays are not supported");
|
||||
|
||||
if (ArrayType.IsGenericEnumerable())
|
||||
throw new InvalidOperationException("Enumerables of enumerables are not supported");
|
||||
|
||||
Column = attr;
|
||||
|
||||
Get = CreateGetter(property);
|
||||
Set = CreateSetter(property);
|
||||
if (attr != null && attr.AllowNull && Type.IsNullableType())
|
||||
attr.AllowNull = false;
|
||||
|
||||
if (property.CanRead)
|
||||
Get = CreateGetter(property);
|
||||
|
||||
if (property.CanWrite)
|
||||
Setter = CreateSetter(property);
|
||||
}
|
||||
|
||||
private Func<object, object> CreateGetter(PropertyInfo property)
|
||||
@@ -72,7 +126,7 @@ namespace DynamORM.Mapper
|
||||
if (!property.CanRead)
|
||||
return null;
|
||||
|
||||
var objParm = Expression.Parameter(typeof(object), "o");
|
||||
ParameterExpression objParm = Expression.Parameter(typeof(object), "o");
|
||||
|
||||
return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Convert(
|
||||
@@ -87,8 +141,8 @@ namespace DynamORM.Mapper
|
||||
if (!property.CanWrite)
|
||||
return null;
|
||||
|
||||
var objParm = Expression.Parameter(typeof(object), "o");
|
||||
var valueParm = Expression.Parameter(typeof(object), "value");
|
||||
ParameterExpression objParm = Expression.Parameter(typeof(object), "o");
|
||||
ParameterExpression valueParm = Expression.Parameter(typeof(object), "value");
|
||||
|
||||
return Expression.Lambda<Action<object, object>>(
|
||||
Expression.Assign(
|
||||
@@ -98,5 +152,128 @@ namespace DynamORM.Mapper
|
||||
Expression.Convert(valueParm, property.PropertyType)),
|
||||
objParm, valueParm).Compile();
|
||||
}
|
||||
|
||||
/// <summary>Sets the specified value to destination object.</summary>
|
||||
/// <param name="dest">The destination object.</param>
|
||||
/// <param name="val">The value.</param>
|
||||
public void Set(object dest, object val, bool byProperty = false)
|
||||
{
|
||||
object value = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (!Type.IsAssignableFrom(val.GetType()))
|
||||
{
|
||||
if (Type.IsArray || IsGnericEnumerable)
|
||||
{
|
||||
if (val != null)
|
||||
{
|
||||
if (val is IEnumerable<object>)
|
||||
{
|
||||
var lst = (val as IEnumerable<object>).Select(x => GetElementVal(ArrayType, x, byProperty)).ToList();
|
||||
|
||||
value = Array.CreateInstance(ArrayType, lst.Count);
|
||||
|
||||
int i = 0;
|
||||
foreach (var e in lst)
|
||||
((Array)value).SetValue(e, i++);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Array.CreateInstance(ArrayType, 1);
|
||||
((Array)value).SetValue(GetElementVal(ArrayType, val, byProperty), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
value = Array.CreateInstance(ArrayType, 0);
|
||||
}
|
||||
else
|
||||
value = GetElementVal(Type, val, byProperty);
|
||||
}
|
||||
else
|
||||
value = val;
|
||||
|
||||
Setter(dest, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DynamicMapperException(
|
||||
string.Format("Error trying to convert and set value '{0}' of type '{1}' to type '{2}' in object of type '{3}'",
|
||||
val == null ? string.Empty : val.ToString(), val.GetType(), Type.FullName, dest.GetType().FullName),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private object GetElementVal(System.Type etype, object val, bool byProperty)
|
||||
{
|
||||
bool nullable = etype.IsGenericType && etype.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
Type type = Nullable.GetUnderlyingType(etype) ?? etype;
|
||||
|
||||
if (val == null && type.IsValueType)
|
||||
{
|
||||
if (nullable)
|
||||
return null;
|
||||
else
|
||||
return Activator.CreateInstance(Type);
|
||||
}
|
||||
else if ((val == null && !type.IsValueType) || (val != null && type == val.GetType()))
|
||||
return val;
|
||||
else if (type.IsEnum && val.GetType().IsValueType)
|
||||
return Enum.ToObject(type, val);
|
||||
else if (type.IsEnum)
|
||||
try
|
||||
{
|
||||
return Enum.Parse(type, val.ToString());
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
if (nullable)
|
||||
return null;
|
||||
|
||||
throw;
|
||||
}
|
||||
else if (Type == typeof(string) && val.GetType() == typeof(Guid))
|
||||
return val.ToString();
|
||||
else if (Type == typeof(Guid))
|
||||
{
|
||||
if (val.GetType() == typeof(byte[]))
|
||||
return new Guid((byte[])val);
|
||||
else if (val.GetType() == typeof(string))
|
||||
{
|
||||
Guid g;
|
||||
return Guid.TryParse((string)val, out g) ? g : Guid.Empty;
|
||||
}
|
||||
else return (nullable) ? null : (object)Guid.Empty;
|
||||
}
|
||||
else if (!typeof(IConvertible).IsAssignableFrom(type) && (IsDataContract || (!type.IsValueType && val is IDictionary<string, object>)))
|
||||
{
|
||||
if (byProperty)
|
||||
return val.MapByProperty(type);
|
||||
|
||||
return val.Map(type);
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
return Convert.ChangeType(val, type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (nullable)
|
||||
return null;
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#region Type command cache
|
||||
|
||||
internal ParameterSpec InsertCommandParameter { get; set; }
|
||||
|
||||
internal ParameterSpec UpdateCommandParameter { get; set; }
|
||||
|
||||
internal ParameterSpec DeleteCommandParameter { get; set; }
|
||||
|
||||
#endregion Type command cache
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,9 +27,12 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using DynamORM.Validation;
|
||||
|
||||
namespace DynamORM.Mapper
|
||||
{
|
||||
@@ -46,9 +49,11 @@ namespace DynamORM.Mapper
|
||||
public Func<object> Creator { get; private set; }
|
||||
|
||||
/// <summary>Gets map of columns to properties.</summary>
|
||||
/// <remarks>Key: Column name (lower), Value: <see cref="DynamicPropertyInvoker"/>.</remarks>
|
||||
public Dictionary<string, DynamicPropertyInvoker> ColumnsMap { get; private set; }
|
||||
|
||||
/// <summary>Gets map of properties to column.</summary>
|
||||
/// <remarks>Key: Property name, Value: Column name.</remarks>
|
||||
public Dictionary<string, string> PropertyMap { get; private set; }
|
||||
|
||||
/// <summary>Gets list of ignored properties.</summary>
|
||||
@@ -60,7 +65,7 @@ namespace DynamORM.Mapper
|
||||
{
|
||||
Type = type;
|
||||
|
||||
var attr = type.GetCustomAttributes(typeof(TableAttribute), false);
|
||||
object[] attr = type.GetCustomAttributes(typeof(TableAttribute), false);
|
||||
|
||||
if (attr != null && attr.Length > 0)
|
||||
Table = (TableAttribute)attr[0];
|
||||
@@ -71,35 +76,47 @@ namespace DynamORM.Mapper
|
||||
|
||||
private void CreateColumnAndPropertyMap()
|
||||
{
|
||||
var columnMap = new Dictionary<string, DynamicPropertyInvoker>();
|
||||
var propertyMap = new Dictionary<string, string>();
|
||||
Dictionary<string, DynamicPropertyInvoker> columnMap = new Dictionary<string, DynamicPropertyInvoker>();
|
||||
Dictionary<string, string> propertyMap = new Dictionary<string, string>();
|
||||
List<string> ignored = new List<string>();
|
||||
|
||||
foreach (var pi in Type.GetProperties())
|
||||
foreach (PropertyInfo pi in GetAllMembers(Type).Where(x => x is PropertyInfo).Cast<PropertyInfo>())
|
||||
{
|
||||
// Skip indexers
|
||||
if (pi.GetIndexParameters().Any())
|
||||
continue;
|
||||
|
||||
ColumnAttribute attr = null;
|
||||
|
||||
var attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
|
||||
object[] attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
|
||||
|
||||
if (attrs != null && attrs.Length > 0)
|
||||
attr = (ColumnAttribute)attrs[0];
|
||||
|
||||
string col = attr == null || string.IsNullOrEmpty(attr.Name) ? pi.Name : attr.Name;
|
||||
|
||||
var val = new DynamicPropertyInvoker(pi, attr);
|
||||
DynamicPropertyInvoker val = new DynamicPropertyInvoker(pi, attr);
|
||||
columnMap.Add(col.ToLower(), val);
|
||||
|
||||
propertyMap.Add(pi.Name, col);
|
||||
|
||||
if (val.Ignore)
|
||||
ignored.Add(pi.Name);
|
||||
}
|
||||
|
||||
ColumnsMap = columnMap;
|
||||
PropertyMap = propertyMap;
|
||||
|
||||
Ignored = columnMap.Where(i => i.Value.Ignore).Select(i => i.Value.Name).ToList();
|
||||
Ignored = ignored; ////columnMap.Where(i => i.Value.Ignore).Select(i => i.Value.Name).ToList();
|
||||
}
|
||||
|
||||
private Func<object> CreateCreator()
|
||||
{
|
||||
if (Type.GetConstructor(Type.EmptyTypes) != null)
|
||||
var c = Type.GetConstructor(Type.EmptyTypes);
|
||||
if (c == null)
|
||||
c = Type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
|
||||
if (c != null)
|
||||
return Expression.Lambda<Func<object>>(Expression.New(Type)).Compile();
|
||||
|
||||
return null;
|
||||
@@ -113,6 +130,15 @@ namespace DynamORM.Mapper
|
||||
return Map(source, Creator());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Create object of <see cref="DynamicTypeMap.Type"/> type and fill values from <c>source</c> using property names.</summary>
|
||||
/// <param name="source">Object containing values that will be mapped to newly created object.</param>
|
||||
/// <returns>New object of <see cref="DynamicTypeMap.Type"/> type with matching values from <c>source</c>.</returns>
|
||||
public object CreateByProperty(object source)
|
||||
{
|
||||
return MapByProperty(source, Creator());
|
||||
}
|
||||
|
||||
/// <summary>Fill values from <c>source</c> to <see cref="DynamicTypeMap.Type"/> object in <c>destination</c>.</summary>
|
||||
/// <param name="source">Object containing values that will be mapped to newly created object.</param>
|
||||
/// <param name="destination">Object of <see cref="DynamicTypeMap.Type"/> type to which copy values from <c>source</c>.</param>
|
||||
@@ -121,14 +147,170 @@ namespace DynamORM.Mapper
|
||||
{
|
||||
DynamicPropertyInvoker dpi = null;
|
||||
|
||||
foreach (var item in source.ToDictionary())
|
||||
foreach (KeyValuePair<string, object> item in source.ToDictionary())
|
||||
{
|
||||
if (ColumnsMap.TryGetValue(item.Key.ToLower(), out dpi) && item.Value != null)
|
||||
if (dpi.Set != null)
|
||||
if (dpi.Setter != null)
|
||||
dpi.Set(destination, item.Value);
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
/// <summary>Fill values from <c>source</c> to <see cref="DynamicTypeMap.Type"/> object in <c>destination</c> using property names.</summary>
|
||||
/// <param name="source">Object containing values that will be mapped to newly created object.</param>
|
||||
/// <param name="destination">Object of <see cref="DynamicTypeMap.Type"/> type to which copy values from <c>source</c>.</param>
|
||||
/// <returns>Object of <see cref="DynamicTypeMap.Type"/> type with matching values from <c>source</c>.</returns>
|
||||
public object MapByProperty(object source, object destination)
|
||||
{
|
||||
string cn = null;
|
||||
DynamicPropertyInvoker dpi = null;
|
||||
|
||||
foreach (KeyValuePair<string, object> item in source.ToDictionary())
|
||||
{
|
||||
if (PropertyMap.TryGetValue(item.Key, out cn) && item.Value != null)
|
||||
if (ColumnsMap.TryGetValue(cn.ToLower(), out dpi) && item.Value != null)
|
||||
if (dpi.Setter != null)
|
||||
dpi.Set(destination, item.Value, true);
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
/// <summary>Validates the object.</summary>
|
||||
/// <param name="val">The value.</param>
|
||||
/// <returns>List of not valid results.</returns>
|
||||
public IList<ValidationResult> ValidateObject(object val)
|
||||
{
|
||||
var result = new List<ValidationResult>();
|
||||
|
||||
if (val == null || val.GetType() != Type)
|
||||
return null;
|
||||
|
||||
foreach (var prop in ColumnsMap.Values)
|
||||
{
|
||||
if (prop.Requirements == null || !prop.Requirements.Any())
|
||||
continue;
|
||||
|
||||
var v = prop.Get(val);
|
||||
|
||||
foreach (var r in prop.Requirements.Where(x => !x.ElementRequirement))
|
||||
{
|
||||
var valid = r.ValidateSimpleValue(prop, v);
|
||||
|
||||
if (valid == ValidateResult.Valid)
|
||||
{
|
||||
if (prop.Type.IsArray || prop.IsGnericEnumerable)
|
||||
{
|
||||
var map = DynamicMapperCache.GetMapper(prop.ArrayType);
|
||||
|
||||
var list = v as IEnumerable<object>;
|
||||
|
||||
if (list == null)
|
||||
{
|
||||
var enumerable = v as IEnumerable;
|
||||
if (enumerable != null)
|
||||
list = enumerable.Cast<object>();
|
||||
}
|
||||
|
||||
if (list != null)
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (prop.Requirements.Any(x => x.ElementRequirement))
|
||||
{
|
||||
foreach (var re in prop.Requirements.Where(x => x.ElementRequirement))
|
||||
{
|
||||
var validelem = re.ValidateSimpleValue(prop.ArrayType, prop.ArrayType.IsGenericEnumerable(), item);
|
||||
|
||||
if (validelem == ValidateResult.NotSupported)
|
||||
{
|
||||
result.AddRange(map.ValidateObject(item));
|
||||
break;
|
||||
}
|
||||
else if (validelem != ValidateResult.Valid)
|
||||
result.Add(new ValidationResult()
|
||||
{
|
||||
Property = prop,
|
||||
Requirement = r,
|
||||
Value = item,
|
||||
Result = validelem,
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
result.AddRange(map.ValidateObject(item));
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (valid == ValidateResult.NotSupported)
|
||||
{
|
||||
result.AddRange(DynamicMapperCache.GetMapper(prop.Type).ValidateObject(v));
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(new ValidationResult()
|
||||
{
|
||||
Property = prop,
|
||||
Requirement = r,
|
||||
Value = v,
|
||||
Result = valid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<MemberInfo> GetAllMembers(Type type)
|
||||
{
|
||||
if (type.IsInterface)
|
||||
{
|
||||
List<MemberInfo> members = new List<MemberInfo>();
|
||||
List<Type> considered = new List<Type>();
|
||||
Queue<Type> queue = new Queue<Type>();
|
||||
|
||||
considered.Add(type);
|
||||
queue.Enqueue(type);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
Type subType = queue.Dequeue();
|
||||
foreach (Type subInterface in subType.GetInterfaces())
|
||||
{
|
||||
if (considered.Contains(subInterface)) continue;
|
||||
|
||||
considered.Add(subInterface);
|
||||
queue.Enqueue(subInterface);
|
||||
}
|
||||
|
||||
MemberInfo[] typeProperties = subType.GetMembers(
|
||||
BindingFlags.FlattenHierarchy
|
||||
| BindingFlags.Public
|
||||
| BindingFlags.Instance);
|
||||
|
||||
IEnumerable<MemberInfo> newPropertyInfos = typeProperties
|
||||
.Where(x => !members.Contains(x));
|
||||
|
||||
members.InsertRange(0, newPropertyInfos);
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
return type.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
|
||||
}
|
||||
|
||||
#region Type command cache
|
||||
|
||||
internal string InsertCommandText { get; set; }
|
||||
|
||||
internal string UpdateCommandText { get; set; }
|
||||
|
||||
internal string DeleteCommandText { get; set; }
|
||||
|
||||
#endregion Type command cache
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -34,6 +34,9 @@ namespace DynamORM.Mapper
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class TableAttribute : Attribute
|
||||
{
|
||||
/// <summary>Gets or sets table owner name.</summary>
|
||||
public string Owner { get; set; }
|
||||
|
||||
/// <summary>Gets or sets name.</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
|
||||
307
DynamORM/Objects/DynamicEntityBase.cs
Normal file
307
DynamORM/Objects/DynamicEntityBase.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Mapper;
|
||||
using DynamORM.Validation;
|
||||
|
||||
namespace DynamORM.Objects
|
||||
{
|
||||
/// <summary>Base class for strong typed objects.</summary>
|
||||
public class DynamicEntityBase
|
||||
{
|
||||
private Dictionary<string, object> _changedFields = new Dictionary<string, object>();
|
||||
private DynamicEntityState _dynamicEntityState = DynamicEntityState.Unknown;
|
||||
|
||||
/// <summary>Occurs when object property is changing.</summary>
|
||||
public event EventHandler<DynamicPropertyChangingEventArgs> PropertyChanging;
|
||||
|
||||
/// <summary>Gets the state of the dynamic entity.</summary>
|
||||
/// <returns>Current state of entity.</returns>
|
||||
public virtual DynamicEntityState GetDynamicEntityState() { return _dynamicEntityState; }
|
||||
|
||||
/// <summary>Sets the state of the dynamic entity.</summary>
|
||||
/// <remarks>Using this method will reset modified fields list.</remarks>
|
||||
/// <param name="state">The state.</param>
|
||||
public virtual void SetDynamicEntityState(DynamicEntityState state)
|
||||
{
|
||||
_dynamicEntityState = state;
|
||||
|
||||
if (_changedFields != null)
|
||||
_changedFields.Clear();
|
||||
}
|
||||
|
||||
/// <summary>Called when object property is changing.</summary>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <param name="oldValue">The old property value.</param>
|
||||
/// <param name="newValue">The new property value.</param>
|
||||
protected virtual void OnPropertyChanging(string propertyName, object oldValue, object newValue)
|
||||
{
|
||||
OnPropertyChanging(new DynamicPropertyChangingEventArgs(propertyName, oldValue, newValue));
|
||||
}
|
||||
|
||||
/// <summary>Raises the <see cref="E:PropertyChanging"/> event.</summary>
|
||||
/// <param name="e">The <see cref="DynamicPropertyChangingEventArgs"/> instance containing the event data.</param>
|
||||
protected virtual void OnPropertyChanging(DynamicPropertyChangingEventArgs e)
|
||||
{
|
||||
_changedFields[e.PropertyName] = e.NewValue;
|
||||
if (PropertyChanging != null)
|
||||
PropertyChanging(this, e);
|
||||
}
|
||||
|
||||
/// <summary>Validates this object instance.</summary>
|
||||
/// <returns>Returns list of <see cref="ValidationResult"/> containing results of validation.</returns>
|
||||
public virtual IList<ValidationResult> Validate()
|
||||
{
|
||||
return DynamicMapperCache.GetMapper(this.GetType()).ValidateObject(this);
|
||||
}
|
||||
|
||||
/// <summary>Saves this object to database.</summary>
|
||||
/// <param name="database">The database.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
/// <exception cref="InvalidOperationException">Can be thrown when object is in invalid state.</exception>
|
||||
public virtual bool Save(DynamicDatabase database)
|
||||
{
|
||||
switch (GetDynamicEntityState())
|
||||
{
|
||||
default:
|
||||
case DynamicEntityState.Unknown:
|
||||
throw new InvalidOperationException("Unknown object state. Unable to decide whish action should be performed.");
|
||||
|
||||
case DynamicEntityState.New:
|
||||
return Insert(database);
|
||||
|
||||
case DynamicEntityState.Existing:
|
||||
if (IsModified())
|
||||
return Update(database);
|
||||
|
||||
return true;
|
||||
|
||||
case DynamicEntityState.ToBeDeleted:
|
||||
return Delete(database);
|
||||
|
||||
case DynamicEntityState.Deleted:
|
||||
throw new InvalidOperationException("Unable to do any database action on deleted object.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Determines whether this instance is in existing state and fields was modified since this state was set modified.</summary>
|
||||
/// <returns>Returns <c>true</c> if this instance is modified; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool IsModified()
|
||||
{
|
||||
if (GetDynamicEntityState() != DynamicEntityState.Existing)
|
||||
return false;
|
||||
|
||||
return _changedFields != null && _changedFields.Any();
|
||||
}
|
||||
|
||||
#region Insert/Update/Delete
|
||||
|
||||
/// <summary>Inserts this object to database.</summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Insert(DynamicDatabase db)
|
||||
{
|
||||
using (var query = db.Insert(this.GetType()))
|
||||
{
|
||||
if (query
|
||||
.Values(x => this)
|
||||
.Execute() > 0)
|
||||
{
|
||||
_changedFields.Clear();
|
||||
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Updates this object in database.</summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Update(DynamicDatabase db)
|
||||
{
|
||||
var t = GetType();
|
||||
var mapper = DynamicMapperCache.GetMapper(t);
|
||||
using (var query = db.Update(t))
|
||||
{
|
||||
MakeQueryWhere(mapper, query);
|
||||
|
||||
bool any = false;
|
||||
|
||||
if (_changedFields.Any())
|
||||
{
|
||||
foreach (var cf in _changedFields)
|
||||
{
|
||||
var cn = mapper.PropertyMap[cf.Key];
|
||||
var pm = mapper.ColumnsMap[cn.ToLower()];
|
||||
if (pm.Ignore)
|
||||
continue;
|
||||
|
||||
if (pm.Column != null)
|
||||
{
|
||||
if (pm.Column.IsKey)
|
||||
continue;
|
||||
|
||||
if (!pm.Column.AllowNull && cf.Value == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
query.Values(cn, cf.Value);
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
foreach (var pmk in mapper.ColumnsMap)
|
||||
{
|
||||
var pm = pmk.Value;
|
||||
var val = pm.Get(this);
|
||||
var cn = pm.Name;
|
||||
|
||||
if (pm.Ignore)
|
||||
continue;
|
||||
|
||||
if (pm.Column != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pm.Column.Name))
|
||||
cn = pm.Column.Name;
|
||||
|
||||
if (pm.Column.IsKey)
|
||||
continue;
|
||||
|
||||
if (!pm.Column.AllowNull && val == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
query.Values(cn, val);
|
||||
}
|
||||
|
||||
if (query.Execute() == 0)
|
||||
return false;
|
||||
|
||||
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||
_changedFields.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Deletes this object from database.</summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Delete(DynamicDatabase db)
|
||||
{
|
||||
var t = this.GetType();
|
||||
var mapper = DynamicMapperCache.GetMapper(t);
|
||||
|
||||
using (var query = db.Delete(t))
|
||||
{
|
||||
MakeQueryWhere(mapper, query);
|
||||
|
||||
if (query.Execute() == 0)
|
||||
return false;
|
||||
|
||||
SetDynamicEntityState(DynamicEntityState.Deleted);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Insert/Update/Delete
|
||||
|
||||
#region Select
|
||||
|
||||
/// <summary>Refresh non key data from database.</summary>
|
||||
/// <param name="db">The database.</param>
|
||||
/// <remarks>All properties that are primary key values must be filled.</remarks>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Refresh(DynamicDatabase db)
|
||||
{
|
||||
var t = this.GetType();
|
||||
var mapper = DynamicMapperCache.GetMapper(t);
|
||||
using (var query = db.From(t))
|
||||
{
|
||||
MakeQueryWhere(mapper, query);
|
||||
var o = (query.Execute() as IEnumerable<dynamic>).FirstOrDefault();
|
||||
|
||||
if (o == null)
|
||||
return false;
|
||||
|
||||
mapper.Map(o, this);
|
||||
|
||||
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||
_changedFields.Clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Select
|
||||
|
||||
#region Query Helpers
|
||||
|
||||
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicUpdateQueryBuilder query)
|
||||
{
|
||||
bool keyNotDefined = true;
|
||||
|
||||
foreach (var cm in mapper.ColumnsMap)
|
||||
{
|
||||
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||
{
|
||||
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||
keyNotDefined = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyNotDefined)
|
||||
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||
this.GetType().FullName));
|
||||
}
|
||||
|
||||
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicDeleteQueryBuilder query)
|
||||
{
|
||||
bool keyNotDefined = true;
|
||||
|
||||
foreach (var cm in mapper.ColumnsMap)
|
||||
{
|
||||
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||
{
|
||||
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||
keyNotDefined = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyNotDefined)
|
||||
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||
this.GetType().FullName));
|
||||
}
|
||||
|
||||
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
bool keyNotDefined = true;
|
||||
|
||||
foreach (var cm in mapper.ColumnsMap)
|
||||
{
|
||||
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||
{
|
||||
var v = cm.Value.Get(this);
|
||||
|
||||
if (v == null)
|
||||
throw new InvalidOperationException(String.Format("Class '{0}' have key columns {1} not filled with data.",
|
||||
this.GetType().FullName, cm.Value.Name));
|
||||
|
||||
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||
keyNotDefined = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyNotDefined)
|
||||
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||
this.GetType().FullName));
|
||||
}
|
||||
|
||||
#endregion Query Helpers
|
||||
}
|
||||
}
|
||||
23
DynamORM/Objects/DynamicEntityState.cs
Normal file
23
DynamORM/Objects/DynamicEntityState.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace DynamORM.Objects
|
||||
{
|
||||
/// <summary>Possible states of dynamic database objects.</summary>
|
||||
public enum DynamicEntityState
|
||||
{
|
||||
/// <summary>Default state. This state will only permit to refresh data from database.</summary>
|
||||
/// <remarks>In this state repository will be unable to tell if object with this state should be added
|
||||
/// or updated in database, but you can still manually perform update or insert on such object.</remarks>
|
||||
Unknown,
|
||||
|
||||
/// <summary>This state should be set to new objects in database.</summary>
|
||||
New,
|
||||
|
||||
/// <summary>This state is ser when data is refreshed from database or object was loaded from repository.</summary>
|
||||
Existing,
|
||||
|
||||
/// <summary>You can set this state to an object if you want repository to perform delete from database.</summary>
|
||||
ToBeDeleted,
|
||||
|
||||
/// <summary>This state is set for objects that were deleted from database.</summary>
|
||||
Deleted,
|
||||
}
|
||||
}
|
||||
32
DynamORM/Objects/DynamicPropertyChangingEventArgs.cs
Normal file
32
DynamORM/Objects/DynamicPropertyChangingEventArgs.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace DynamORM.Objects
|
||||
{
|
||||
/// <summary>Class containing changed property data.</summary>
|
||||
/// <seealso cref="System.EventArgs" />
|
||||
public class DynamicPropertyChangingEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>Gets the name of the property.</summary>
|
||||
/// <value>The name of the property.</value>
|
||||
public string PropertyName { get; private set; }
|
||||
|
||||
/// <summary>Gets the old property value.</summary>
|
||||
/// <value>The old value.</value>
|
||||
public object OldValue { get; private set; }
|
||||
|
||||
/// <summary>Gets the new property value.</summary>
|
||||
/// <value>The new value.</value>
|
||||
public object NewValue { get; private set; }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicPropertyChangingEventArgs"/> class.</summary>
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <param name="oldValue">The old property value.</param>
|
||||
/// <param name="newValue">The new property value.</param>
|
||||
public DynamicPropertyChangingEventArgs(string propertyName, object oldValue, object newValue)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
DynamORM/Objects/DynamicRepositoryBase.cs
Normal file
129
DynamORM/Objects/DynamicRepositoryBase.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Objects
|
||||
{
|
||||
/// <summary>Base repository class for specified object type.</summary>
|
||||
/// <typeparam name="T">Type of stored object.</typeparam>
|
||||
public class DynamicRepositoryBase<T> : IDisposable where T : DynamicEntityBase
|
||||
{
|
||||
private DynamicDatabase _database;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicRepositoryBase{T}"/> class.</summary>
|
||||
/// <param name="database">The database.</param>
|
||||
public DynamicRepositoryBase(DynamicDatabase database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
/// <summary>Get all rows from database.</summary>
|
||||
/// <returns>Objects enumerator.</returns>
|
||||
public virtual IEnumerable<T> GetAll()
|
||||
{
|
||||
using (var q = _database.From<T>())
|
||||
return EnumerateQuery(q);
|
||||
}
|
||||
|
||||
/// <summary>Get rows from database by custom query.</summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <remarks>Query must be based on object type.</remarks>
|
||||
/// <returns>Objects enumerator.</returns>
|
||||
public virtual IEnumerable<T> GetByQuery(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return EnumerateQuery(query);
|
||||
}
|
||||
|
||||
private IEnumerable<T> EnumerateQuery(IDynamicSelectQueryBuilder query, bool forceType = true)
|
||||
{
|
||||
if (forceType)
|
||||
{
|
||||
var mapper = DynamicMapperCache.GetMapper(typeof(T));
|
||||
|
||||
var tn = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||
mapper.Type.Name : mapper.Table.Name;
|
||||
|
||||
if (!query.Tables.Any(t => t.Name == tn))
|
||||
throw new InvalidOperationException(string.Format("Query is not related to '{0}' class.", typeof(T).FullName));
|
||||
}
|
||||
|
||||
foreach (var o in query.Execute<T>())
|
||||
{
|
||||
o.SetDynamicEntityState(DynamicEntityState.Existing);
|
||||
yield return o;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Saves single object to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Save(T element)
|
||||
{
|
||||
return element.Save(_database);
|
||||
}
|
||||
|
||||
/// <summary>Saves collection of objects to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Save(IEnumerable<T> element)
|
||||
{
|
||||
return element.All(x => x.Save(_database));
|
||||
}
|
||||
|
||||
/// <summary>Insert single object to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Insert(T element)
|
||||
{
|
||||
return element.Insert(_database);
|
||||
}
|
||||
|
||||
/// <summary>Insert collection of objects to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Insert(IEnumerable<T> element)
|
||||
{
|
||||
return element.All(x => x.Insert(_database));
|
||||
}
|
||||
|
||||
/// <summary>Update single object to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Update(T element)
|
||||
{
|
||||
return element.Update(_database);
|
||||
}
|
||||
|
||||
/// <summary>Update collection of objects to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Update(IEnumerable<T> element)
|
||||
{
|
||||
return element.All(x => x.Update(_database));
|
||||
}
|
||||
|
||||
/// <summary>Delete single object to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Delete(T element)
|
||||
{
|
||||
return element.Delete(_database);
|
||||
}
|
||||
|
||||
/// <summary>Delete collection of objects to database.</summary>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||
public virtual bool Delete(IEnumerable<T> element)
|
||||
{
|
||||
return element.All(x => x.Delete(_database));
|
||||
}
|
||||
|
||||
/// <summary>Releases unmanaged and - optionally - managed resources.</summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_database = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,40 +26,19 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* See: http://opensource.org/licenses/bsd-license.php
|
||||
*
|
||||
* Supported preprocessor flags:
|
||||
* * DYNAMORM_OMMIT_OLDSYNTAX - Remove dynamic table functionality
|
||||
* * DYNAMORM_OMMIT_GENERICEXECUTION - Remove generic execution functionality
|
||||
* * DYNAMORM_OMMIT_TRYPARSE - Remove TryParse helpers (also applies DYNAMORM_OMMIT_GENERICEXECUTION)
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DynamORM")]
|
||||
[assembly: AssemblyDescription("Dynamic Object-Relational Mapping library.")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("RUSSEK Software")]
|
||||
[assembly: AssemblyProduct("DynamORM")]
|
||||
[assembly: AssemblyCopyright("Copyright © RUSSEK Software 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("0a42480b-bba7-4b01-807f-9962a76e4e47")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.1")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.1")]
|
||||
[assembly: InternalsVisibleTo("DynamORM.Tests")]
|
||||
[assembly: Obfuscation(Feature = "encrypt symbol names with password #dr4cul4#", Exclude = false)]
|
||||
[assembly: Obfuscation(Feature = "code control flow obfuscation", Exclude = false)]
|
||||
[assembly: Obfuscation(Feature = "rename serializable symbols", Exclude = false)]
|
||||
[assembly: Obfuscation(Feature = "anonymous type properties renaming", Exclude = true)]
|
||||
[assembly: Obfuscation(Feature = "optimization", Exclude = true)]
|
||||
160
DynamORM/Validation/RequiredAttribute.cs
Normal file
160
DynamORM/Validation/RequiredAttribute.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Validation
|
||||
{
|
||||
/// <summary>Required attribute can be used to validate fields in objects using mapper class.</summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
|
||||
public class RequiredAttribute : Attribute
|
||||
{
|
||||
/// <summary>Gets or sets minimum value or length of field.</summary>
|
||||
public decimal? Min { get; set; }
|
||||
|
||||
/// <summary>Gets or sets maximum value or length of field.</summary>
|
||||
public decimal? Max { get; set; }
|
||||
|
||||
/// <summary>Gets or sets pattern to verify.</summary>
|
||||
public Regex Pattern { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether property value is required or not.</summary>
|
||||
public bool Required { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether this is an element requirement.</summary>
|
||||
public bool ElementRequirement { get; set; }
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
|
||||
/// <param name="required">This field will be required.</param>
|
||||
public RequiredAttribute(bool required = true)
|
||||
{
|
||||
Required = required;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
|
||||
/// <param name="val">Limiting value to set.</param>
|
||||
/// <param name="max">Whether set maximum parameter (true) or minimum parameter (false).</param>
|
||||
/// <param name="required">This field will be required.</param>
|
||||
public RequiredAttribute(float val, bool max, bool required = true)
|
||||
{
|
||||
if (max)
|
||||
Max = (decimal)val;
|
||||
else
|
||||
Min = (decimal)val;
|
||||
Required = required;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
|
||||
/// <param name="min">Minimum value to set.</param>
|
||||
/// <param name="max">Maximum value to set.</param>
|
||||
/// <param name="required">This field will be required.</param>
|
||||
public RequiredAttribute(float min, float max, bool required = true)
|
||||
{
|
||||
Min = (decimal)min;
|
||||
Max = (decimal)max;
|
||||
Required = required;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RequiredAttribute" /> class.</summary>
|
||||
/// <param name="min">Minimum value to set.</param>
|
||||
/// <param name="max">Maximum value to set.</param>
|
||||
/// <param name="pattern">Pattern to check.</param>
|
||||
/// <param name="required">This field will be required.</param>
|
||||
public RequiredAttribute(float min, float max, string pattern, bool required = true)
|
||||
{
|
||||
Min = (decimal)min;
|
||||
Max = (decimal)max;
|
||||
Pattern = new Regex(pattern, RegexOptions.Compiled);
|
||||
Required = required;
|
||||
}
|
||||
|
||||
internal ValidateResult ValidateSimpleValue(DynamicPropertyInvoker dpi, object val)
|
||||
{
|
||||
return ValidateSimpleValue(dpi.Type, dpi.IsGnericEnumerable, val);
|
||||
}
|
||||
|
||||
internal ValidateResult ValidateSimpleValue(Type type, bool isGnericEnumerable, object val)
|
||||
{
|
||||
if (val == null)
|
||||
{
|
||||
if (Required)
|
||||
return ValidateResult.ValueIsMissing;
|
||||
else
|
||||
return ValidateResult.Valid;
|
||||
}
|
||||
|
||||
if (type.IsValueType)
|
||||
{
|
||||
if (val is decimal || val is long || val is int || val is float || val is double || val is short || val is byte ||
|
||||
val is decimal? || val is long? || val is int? || val is float? || val is double? || val is short? || val is byte?)
|
||||
{
|
||||
decimal dec = Convert.ToDecimal(val);
|
||||
|
||||
if (Min.HasValue && Min.Value > dec)
|
||||
return ValidateResult.ValueTooSmall;
|
||||
|
||||
if (Max.HasValue && Max.Value < dec)
|
||||
return ValidateResult.ValueTooLarge;
|
||||
|
||||
return ValidateResult.Valid;
|
||||
}
|
||||
else
|
||||
{
|
||||
var str = val.ToString();
|
||||
|
||||
if (Min.HasValue && Min.Value > str.Length)
|
||||
return ValidateResult.ValueTooShort;
|
||||
|
||||
if (Max.HasValue && Max.Value < str.Length)
|
||||
return ValidateResult.ValueTooLong;
|
||||
|
||||
if (Pattern != null && !Pattern.IsMatch(str))
|
||||
return ValidateResult.ValueDontMatchPattern;
|
||||
|
||||
return ValidateResult.Valid;
|
||||
}
|
||||
}
|
||||
else if (type.IsArray || isGnericEnumerable)
|
||||
{
|
||||
int? cnt = null;
|
||||
|
||||
var list = val as IEnumerable<object>;
|
||||
if (list != null)
|
||||
cnt = list.Count();
|
||||
else
|
||||
{
|
||||
var enumerable = val as IEnumerable;
|
||||
if (enumerable != null)
|
||||
cnt = enumerable.Cast<object>().Count();
|
||||
}
|
||||
|
||||
if (Min.HasValue && Min.Value > cnt)
|
||||
return ValidateResult.TooFewElementsInCollection;
|
||||
|
||||
if (Max.HasValue && Max.Value < cnt)
|
||||
return ValidateResult.TooManyElementsInCollection;
|
||||
|
||||
return ValidateResult.Valid;
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
var str = (string)val;
|
||||
|
||||
if (Min.HasValue && Min.Value > str.Length)
|
||||
return ValidateResult.ValueTooShort;
|
||||
|
||||
if (Max.HasValue && Max.Value < str.Length)
|
||||
return ValidateResult.ValueTooLong;
|
||||
|
||||
if (Pattern != null && !Pattern.IsMatch(str))
|
||||
return ValidateResult.ValueDontMatchPattern;
|
||||
|
||||
return ValidateResult.Valid;
|
||||
}
|
||||
|
||||
return ValidateResult.NotSupported;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
DynamORM/Validation/ValidateResult.cs
Normal file
36
DynamORM/Validation/ValidateResult.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace DynamORM.Validation
|
||||
{
|
||||
/// <summary>Validation result enum.</summary>
|
||||
public enum ValidateResult
|
||||
{
|
||||
/// <summary>The valid value.</summary>
|
||||
Valid,
|
||||
|
||||
/// <summary>The value is missing.</summary>
|
||||
ValueIsMissing,
|
||||
|
||||
/// <summary>The value too small.</summary>
|
||||
ValueTooSmall,
|
||||
|
||||
/// <summary>The value too large.</summary>
|
||||
ValueTooLarge,
|
||||
|
||||
/// <summary>The too few elements in collection.</summary>
|
||||
TooFewElementsInCollection,
|
||||
|
||||
/// <summary>The too many elements in collection.</summary>
|
||||
TooManyElementsInCollection,
|
||||
|
||||
/// <summary>The value too short.</summary>
|
||||
ValueTooShort,
|
||||
|
||||
/// <summary>The value too long.</summary>
|
||||
ValueTooLong,
|
||||
|
||||
/// <summary>The value don't match pattern.</summary>
|
||||
ValueDontMatchPattern,
|
||||
|
||||
/// <summary>The not supported.</summary>
|
||||
NotSupported,
|
||||
}
|
||||
}
|
||||
20
DynamORM/Validation/ValidationResult.cs
Normal file
20
DynamORM/Validation/ValidationResult.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Validation
|
||||
{
|
||||
/// <summary>Validation result.</summary>
|
||||
public class ValidationResult
|
||||
{
|
||||
/// <summary>Gets the property invoker.</summary>
|
||||
public DynamicPropertyInvoker Property { get; internal set; }
|
||||
|
||||
/// <summary>Gets the requirement definition.</summary>
|
||||
public RequiredAttribute Requirement { get; internal set; }
|
||||
|
||||
/// <summary>Gets the value that is broken.</summary>
|
||||
public object Value { get; internal set; }
|
||||
|
||||
/// <summary>Gets the result.</summary>
|
||||
public ValidateResult Result { get;internal set;}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
|
||||
26
DynamORM/obj/Debug/net472/DynamORM.AssemblyInfo.cs
Normal file
26
DynamORM/obj/Debug/net472/DynamORM.AssemblyInfo.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("RUSSEK Software")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright © RUSSEK Software 2012-2023")]
|
||||
[assembly: System.Reflection.AssemblyDescriptionAttribute("Dynamic Object-Relational Mapping library.")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.3.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.3.0")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("DynamORM")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("DynamORM")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.3.0.0")]
|
||||
[assembly: System.Reflection.AssemblyMetadataAttribute("RepositoryUrl", "https://git.dr4cul4.pl/RUSSEK-Software/DynamORM")]
|
||||
|
||||
// Generated by the MSBuild WriteCodeFragment class.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
fc853c749cf501759ad6a0d5531aafc846ac0fde
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user