Merge branch 'feature/typed-multitable-scope'

This commit is contained in:
root
2026-02-27 13:44:00 +01:00
43 changed files with 12370 additions and 67 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
<Copyright>Copyright © RUSSEK Software 2012-2026</Copyright>
<Company>RUSSEK Software</Company>
<Authors>Grzegorz Russek</Authors>
<VersionPrefix>1.9</VersionPrefix>
<VersionPrefix>2.0</VersionPrefix>
<Product>DynamORM</Product>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -6,7 +6,7 @@
<Copyright>Copyright © RUSSEK Software 2012-2026</Copyright>
<Company>RUSSEK Software</Company>
<Authors>Grzegorz Russek</Authors>
<VersionPrefix>1.8</VersionPrefix>
<VersionPrefix>2.0</VersionPrefix>
<RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl>
<PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl>
<Product>DynamORM</Product>

View File

@@ -0,0 +1,42 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, 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;
namespace DynamORM.Tests.Helpers
{
[Table(Name = "sample_users")]
public class TypedFluentUser
{
[Column("id_user", true)]
public long Id { get; set; }
[Column("user_code")]
public string Code { get; set; }
}
}

View File

@@ -0,0 +1,33 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using DynamORM.Mapper;
namespace DynamORM.Tests.Helpers
{
[Table(Name = "Users", Owner = "dbo")]
public class TypedJoinUser
{
[Column("Id_User", true)]
public long IdUser { get; set; }
[Column("Active")]
public int Active { get; set; }
}
[Table(Name = "UserClients", Owner = "dbo")]
public class TypedJoinUserClient
{
[Column("User_Id", true)]
public long UserId { get; set; }
[Column("Users")]
public string Users { get; set; }
[Column("Deleted")]
public int Deleted { get; set; }
}
}

View File

@@ -0,0 +1,49 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using DynamORM.Mapper;
namespace DynamORM.Tests.Helpers
{
[Table(Name = "UserRoles", Owner = "dbo")]
public class TypedJoinUserRole
{
[Column("Role_Id", true)]
public long RoleId { get; set; }
[Column("User_Id")]
public long UserId { get; set; }
[Column("Role_Name")]
public string RoleName { get; set; }
}
[Table(Name = "UserRegions", Owner = "dbo")]
public class TypedJoinUserRegion
{
[Column("Region_Id", true)]
public long RegionId { get; set; }
[Column("User_Id")]
public long UserId { get; set; }
[Column("Region_Code")]
public string RegionCode { get; set; }
}
[Table(Name = "UserDepartments", Owner = "dbo")]
public class TypedJoinUserDepartment
{
[Column("Department_Id", true)]
public long DepartmentId { get; set; }
[Column("Region_Id")]
public long RegionId { get; set; }
[Column("Department_Name")]
public string DepartmentName { get; set; }
}
}

View File

@@ -66,7 +66,7 @@ namespace DynamORM.Tests.Modify
/// Tests the basic insert.
/// </summary>
[Test]
public void TestInsertBasic()
public void TestTypedInsertBasic()
{
IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users");

View File

@@ -0,0 +1,73 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using DynamORM.Tests.Helpers;
using NUnit.Framework;
namespace DynamORM.Tests.Modify
{
[TestFixture]
public class TypedModifyExtensionsTests : TestsBase
{
[SetUp]
public void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset |
DynamicDatabaseOptions.SupportSchema);
}
[TearDown]
public void TearDown()
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
[Test]
public void TestTypedUpdateBuilderSetAndWhere()
{
var cmd = Database.UpdateTyped<Users>()
.Set(u => u.Code, "778")
.Where(u => u.Id == 1 && u.Code == "1");
Assert.AreEqual(
string.Format("UPDATE \"sample_users\" SET \"code\" = [${0}] WHERE (\"id\" = [${1}]) AND (\"code\" = [${2}])",
cmd.Parameters.Keys.ElementAt(0),
cmd.Parameters.Keys.ElementAt(1),
cmd.Parameters.Keys.ElementAt(2)),
cmd.CommandText());
}
[Test]
public void TestTypedDeleteBuilderWhere()
{
var cmd = Database.DeleteTyped<Users>()
.Where(u => u.Id == 3);
Assert.AreEqual(
string.Format("DELETE FROM \"sample_users\" WHERE (\"id\" = [${0}])", cmd.Parameters.Keys.First()),
cmd.CommandText());
}
[Test]
public void TestTypedInsertBuilderColumns()
{
var cmd = Database.InsertTyped<Users>()
.Insert(u => u.Code, "901")
.Insert(u => u.First, "TypedB");
Assert.AreEqual(
string.Format("INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ([${0}], [${1}])",
cmd.Parameters.Keys.ElementAt(0),
cmd.Parameters.Keys.ElementAt(1)),
cmd.CommandText());
}
}
}

View File

@@ -0,0 +1,224 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, 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 NUnit.Framework;
using DynamORM.Tests.Helpers;
using System.Collections.Generic;
using static System.Data.Entity.Infrastructure.Design.Executor;
using System.Runtime.InteropServices;
namespace DynamORM.Tests.Modify
{
/// <summary>New parser tests.</summary>
[TestFixture]
public class TypedParserTests : TestsBase
{
/// <summary>Setup test parameters.</summary>
[SetUp]
public virtual void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset);
}
/// <summary>Tear down test objects.</summary>
[TearDown]
public virtual void TearDown()
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
#region Insert
/// <summary>
/// Tests the basic insert.
/// </summary>
[Test]
public void TestTypedInsertBasic()
{
var cmd = new DynamicTypedInsertQueryBuilder<Users>(Database);
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>
[Test]
public void TestInsertSubQuery()
{
var cmd = new DynamicTypedInsertQueryBuilder<Users>(Database);
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>
[Test]
public void TestInsertBasicObject()
{
var cmd = new DynamicTypedInsertQueryBuilder<Users>(Database);
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>
[Test]
public void TestInsertSubQueryObject()
{
var cmd = new DynamicTypedInsertQueryBuilder<Users>(Database);
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>
[Test]
public void TestUpdateBasicSet()
{
var cmd = new DynamicTypedUpdateQueryBuilder<Users>(Database);
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 basic update.
/// </summary>
[Test]
public void TestUpdateBasicValues()
{
var cmd = new DynamicTypedUpdateQueryBuilder<Users>(Database);
cmd
.Values("Code", "001")
.Values("Name", "Admin")
.Values("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>
[Test]
public void TestUpdateSubQuery()
{
var cmd = new DynamicTypedUpdateQueryBuilder<Users>(Database);
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>
[Test]
public void TestUpdateBasicObject()
{
var cmd = new DynamicTypedUpdateQueryBuilder<Users>(Database);
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>
[Test]
public void TestUpdateSubQueryObject()
{
var cmd = new DynamicTypedUpdateQueryBuilder<Users>(Database);
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
}
}

View File

@@ -0,0 +1,142 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, 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.Builders;
using DynamORM.Tests.Helpers;
using NUnit.Framework;
namespace DynamORM.Tests.Select
{
[TestFixture]
public class TypedFluentBuilderTests : TestsBase
{
[SetUp]
public void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset);
}
[TearDown]
public void TearDown()
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
[Test]
public void TestTypedWhereAndSelectUsesPropertyMap()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Where(u => u.Id == 1)
.Select(u => u.Code.As("CodeAlias"))
.OrderBy(u => u.Code.Desc());
Assert.AreEqual(
string.Format("SELECT u.\"user_code\" AS \"CodeAlias\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" = [${0}]) ORDER BY u.\"user_code\" DESC", cmd.Parameters.Keys.First()),
cmd.CommandText());
}
[Test]
public void TestTypedWhereSupportsContains()
{
var ids = new[] { 1L, 2L, 3L }.ToList();
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Where(u => ids.Contains(u.Id))
.Select(u => u.Id);
Assert.AreEqual(
string.Format("SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" IN([${0}], [${1}], [${2}]))",
cmd.Parameters.Keys.ElementAt(0),
cmd.Parameters.Keys.ElementAt(1),
cmd.Parameters.Keys.ElementAt(2)),
cmd.CommandText());
}
[Test]
public void TestTypedGroupByHavingOrderBy()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Select(u => u.Code)
.GroupBy(u => u.Code)
.Having(u => u.Code != null)
.OrderBy(u => u.Code.Asc());
Assert.AreEqual("SELECT u.\"user_code\" FROM \"sample_users\" AS u GROUP BY u.\"user_code\" HAVING (u.\"user_code\" IS NOT NULL) ORDER BY u.\"user_code\" ASC",
cmd.CommandText());
}
[Test]
public void TestTypedJoin()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Inner().As("x").On((l, r) => l.Id == r.Id))
.Select(u => u.Id);
Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u INNER JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")",
cmd.CommandText());
}
[Test]
public void TestTypedLeftJoin()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Left().As("x").On((l, r) => l.Id == r.Id))
.Select(u => u.Id);
Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")",
cmd.CommandText());
}
[Test]
public void TestTypedRightJoin()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Right().As("x").On((l, r) => l.Id == r.Id))
.Select(u => u.Id);
Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u RIGHT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")",
cmd.CommandText());
}
[Test]
public void TestTypedFullJoin()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Full().As("x").On((l, r) => l.Id == r.Id))
.Select(u => u.Id);
Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u FULL JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")",
cmd.CommandText());
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System.Linq;
using DynamORM.Tests.Helpers;
using NUnit.Framework;
namespace DynamORM.Tests.Select
{
[TestFixture]
public class TypedFluentJoinSyntaxTests : TestsBase
{
[SetUp]
public void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportNoLock);
}
[TearDown]
public void TearDown()
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
[Test]
public void TestTypedJoinDefaultJoinKeyword()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr")
.Join<TypedJoinUserClient>(j => j.As("uc").On((u, c) => u.IdUser == c.UserId));
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText());
}
[Test]
public void TestTypedInnerJoinWithAndNull()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr")
.Join<TypedJoinUserClient>(j => j.Inner().As("uc").On((u, c) => u.IdUser == c.UserId && c.Users != null))
.Select(u => u.IdUser);
Assert.AreEqual("SELECT usr.\"Id_User\" 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());
}
[Test]
public void TestTypedJoinWithNoLock()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr", noLock: true)
.Join<TypedJoinUserClient>(j => j.Inner().As("uc").NoLock().On((u, c) => u.IdUser == c.UserId));
Assert.AreEqual("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());
}
[Test]
public void TestTypedLeftOuterJoin()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr")
.Join<TypedJoinUserClient>(j => j.LeftOuter().As("uc").On((u, c) => u.IdUser == c.UserId));
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText());
}
[Test]
public void TestTypedRightOuterJoin()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr")
.Join<TypedJoinUserClient>(j => j.RightOuter().As("uc").On((u, c) => u.IdUser == c.UserId));
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText());
}
[Test]
public void TestTypedCustomJoinTypeCrossApply()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr")
.Join<TypedJoinUserClient>(j => j.Type("CROSS APPLY").As("uc"));
Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr CROSS APPLY \"dbo\".\"UserClients\" AS uc", cmd.CommandText());
}
[Test]
public void TestTypedJoinAndWhereParameterOrder()
{
var cmd = Database.FromTyped<TypedJoinUser>("usr")
.Join<TypedJoinUserClient>(j => j.As("uc").On((u, c) => u.IdUser == c.UserId && c.Deleted == 0))
.Where(u => u.Active == 1);
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.ElementAt(0),
cmd.Parameters.Keys.ElementAt(1)),
cmd.CommandText());
}
}
}

View File

@@ -0,0 +1,860 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, 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 DynamORM.Tests.Helpers;
using NUnit.Framework;
namespace DynamORM.Tests.Select
{
/// <summary>
/// New parser tests.
/// </summary>
[TestFixture]
public class TypedParserTests : TestsBase
{
/// <summary>Setup test parameters.</summary>
[SetUp]
public virtual void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset |
DynamicDatabaseOptions.SupportNoLock);
}
/// <summary>Tear down test objects.</summary>
[TearDown]
public virtual void TearDown()
{
try
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
catch { }
}
/// <summary>
/// Tests from typed method.
/// </summary>
[Test]
public void TestFromGetTyped()
{
var cmd = Database.From<Users>();
Assert.AreEqual("SELECT * FROM \"sample_users\"", cmd.CommandText());
}
/// <summary>
/// Tests from typed method.
/// </summary>
[Test]
public void TestFromGetTypedAs()
{
var cmd = Database.From<Users>("u");
Assert.AreEqual("SELECT * FROM \"sample_users\" AS u", cmd.CommandText());
}
/// <summary>
/// Tests from method using invoke with sub query.
/// </summary>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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>
[Test]
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());
}
}
}

View File

@@ -0,0 +1,502 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System.Linq;
using System.Text.RegularExpressions;
using DynamORM.Tests.Helpers;
using DynamORM.TypedSql;
using NUnit.Framework;
namespace DynamORM.Tests.TypedSql
{
[TestFixture]
public class TypedSqlDslTests : TestsBase
{
private static string NormalizeSql(string sql)
{
int index = 0;
return Regex.Replace(sql, @"\[\$[^\]]+\]", m => string.Format("[${0}]", index++));
}
[SetUp]
public void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset |
DynamicDatabaseOptions.SupportNoLock);
}
[TearDown]
public void TearDown()
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
[Test]
public void TestSelectSqlWithFunctionsAndOrdering()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(
u => Sql.Count().As("cnt"),
u => Sql.Coalesce<string>(u.Col(x => x.Code), Sql.Val("N/A")).As("code_value"))
.GroupBySql(u => Sql.Coalesce<string>(u.Col(x => x.Code), Sql.Val("N/A")))
.HavingSql(u => Sql.Count().Gt(1))
.OrderBySql(u => Sql.Count().Desc());
Assert.AreEqual(
"SELECT COUNT(*) AS \"cnt\", COALESCE(u.\"user_code\", [$0]) AS \"code_value\" FROM \"sample_users\" AS u GROUP BY COALESCE(u.\"user_code\", [$1]) HAVING (COUNT(*) > [$2]) ORDER BY COUNT(*) DESC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlWithCase()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => Sql.Case<string>()
.When(u.Col(x => x.Code).Eq("A"), "Alpha")
.When(u.Col(x => x.Code).Eq("B"), "Beta")
.Else("Other")
.As("category"));
Assert.AreEqual(
"SELECT CASE WHEN (u.\"user_code\" = [$0]) THEN [$1] WHEN (u.\"user_code\" = [$2]) THEN [$3] ELSE [$4] END AS \"category\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestJoinOnSql()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id)).And(r.Col(a => a.Code).IsNotNull())))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON ((u.\"id_user\" = x.\"id_user\") AND (x.\"user_code\" IS NOT NULL))",
cmd.CommandText());
}
[Test]
public void TestUpdateSetSqlAndWhereSql()
{
var cmd = Database.UpdateTyped<Users>()
.SetSql(u => u.Code, u => Sql.Coalesce<string>(Sql.Val("900"), u.Col(x => x.Code)))
.WhereSql(u => u.Col(x => x.Id).Eq(1).And(u.Col(x => x.Code).IsNotNull()));
Assert.AreEqual(
"UPDATE \"sample_users\" SET \"code\" = COALESCE([$0], \"code\") WHERE ((\"id\" = [$1]) AND (\"code\" IS NOT NULL))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestInsertSqlSupportsCase()
{
var cmd = Database.InsertTyped<Users>()
.InsertSql(u => u.Code, u => Sql.Coalesce<string>(Sql.Val("901"), Sql.Val("fallback")))
.InsertSql(u => u.First, u => Sql.Case<string>().When(Sql.Val(1).Eq(1), "Typed").Else("Other"));
Assert.AreEqual(
"INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES (COALESCE([$0], [$1]), CASE WHEN ([$2] = [$3]) THEN [$4] ELSE [$5] END)",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestDeleteWhereSql()
{
var cmd = Database.DeleteTyped<Users>()
.WhereSql(u => u.Col(x => x.Id).Eq(2).And(u.Col(x => x.Code).NotEq("X")));
Assert.AreEqual(
"DELETE FROM \"sample_users\" WHERE ((\"id\" = [$0]) AND (\"code\" <> [$1]))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsLikeInAndBetween()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => u.Col(x => x.Code).Like("A%")
.And(u.Col(x => x.Id).In(1, 2, 3))
.And(u.Col(x => x.Id).Between(1, 10)))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (((u.\"user_code\" LIKE [$0]) AND (u.\"id_user\" IN([$1], [$2], [$3]))) AND (u.\"id_user\" BETWEEN [$4] AND [$5]))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsNotIn()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => u.Col(x => x.Id).NotIn(1, 2))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" NOT IN([$0], [$1]))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsEmptyNotIn()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => u.Col(x => x.Id).NotIn(new int[0]))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (1 = 1)",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsPatternHelpers()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => u.Col(x => x.Code).StartsWith("AB")
.And(u.Col(x => x.Code).EndsWith("YZ"))
.And(u.Col(x => x.Code).Contains("MID")))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (((u.\"user_code\" LIKE [$0]) AND (u.\"user_code\" LIKE [$1])) AND (u.\"user_code\" LIKE [$2]))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsEmptyIn()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => u.Col(x => x.Id).In(new int[0]))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (1 = 0)",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsStandardFunctions()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(
u => Sql.Sum<int>(u.Col(x => x.Id)).As("sum_id"),
u => Sql.Avg<int>(u.Col(x => x.Id)).As("avg_id"),
u => Sql.Min<int>(u.Col(x => x.Id)).As("min_id"),
u => Sql.Max<int>(u.Col(x => x.Id)).As("max_id"),
u => Sql.Abs<int>(Sql.Val(-5)).As("abs_value"),
u => Sql.Upper(u.Col(x => x.Code)).As("upper_code"),
u => Sql.Lower(u.Col(x => x.Code)).As("lower_code"),
u => Sql.Trim(u.Col(x => x.Code)).As("trim_code"),
u => Sql.Length(u.Col(x => x.Code)).As("len_code"),
u => Sql.NullIf<string>(u.Col(x => x.Code), Sql.Val("X")).As("nullif_code"),
u => Sql.CurrentTimestamp().As("ts"));
Assert.AreEqual(
"SELECT SUM(u.\"id_user\") AS \"sum_id\", AVG(u.\"id_user\") AS \"avg_id\", MIN(u.\"id_user\") AS \"min_id\", MAX(u.\"id_user\") AS \"max_id\", ABS([$0]) AS \"abs_value\", UPPER(u.\"user_code\") AS \"upper_code\", LOWER(u.\"user_code\") AS \"lower_code\", TRIM(u.\"user_code\") AS \"trim_code\", LENGTH(u.\"user_code\") AS \"len_code\", NULLIF(u.\"user_code\", [$1]) AS \"nullif_code\", CURRENT_TIMESTAMP AS \"ts\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsArithmeticExpressions()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(
u => (u.Col(x => x.Id) + Sql.Val(1)).As("plus_one"),
u => u.Col(x => x.Id).Sub(2).As("minus_two"),
u => u.Col(x => x.Id).Mul(3).As("times_three"),
u => u.Col(x => x.Id).Div(4).As("div_four"),
u => u.Col(x => x.Id).Mod(5).As("mod_five"));
Assert.AreEqual(
"SELECT (u.\"id_user\" + [$0]) AS \"plus_one\", (u.\"id_user\" - [$1]) AS \"minus_two\", (u.\"id_user\" * [$2]) AS \"times_three\", (u.\"id_user\" / [$3]) AS \"div_four\", (u.\"id_user\" % [$4]) AS \"mod_five\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsCustomFunction()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => Sql.Func<string>("CUSTOM_FUNC", u.Col(x => x.Code), Sql.Val(5)).As("custom_value"));
Assert.AreEqual(
"SELECT CUSTOM_FUNC(u.\"user_code\", [$0]) AS \"custom_value\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsWildcardAll()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => u.All());
Assert.AreEqual(
"SELECT u.* FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsJoinedAliasHelpers()
{
var other = Sql.Table<TypedFluentUser>("x");
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id))))
.SelectSql(
u => u.All(),
u => other.All(),
u => other.Col(x => x.Code).As("joined_code"));
Assert.AreEqual(
"SELECT u.*, x.*, x.\"user_code\" AS \"joined_code\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsJoinedAliasHelpers()
{
var other = Sql.Table<TypedFluentUser>("x");
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id))))
.WhereSql(u => other.Col(x => x.Code).IsNotNull())
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\") WHERE (x.\"user_code\" IS NOT NULL)",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsScalarSubQuery()
{
var sq = Database.From(x => x.sample_users.As("x"))
.Select(x => x.x.id_user)
.Where(x => x.x.user_code == "A");
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => Sql.SubQuery<long>(sq).As("sub_id"));
Assert.AreEqual(
"SELECT (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])) AS \"sub_id\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsExists()
{
var sq = Database.From(x => x.sample_users.As("x"))
.Select(x => x.x.id_user)
.Where(x => x.x.user_code == "A");
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => Sql.Exists(sq))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsTypedSubQueryHelper()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => Sql.SubQuery<TypedFluentUser, long>(
Database,
sq => sq
.SelectSql(x => x.Col(a => a.Id))
.WhereSql(x => x.Col(a => a.Code).Eq("A")),
"x").As("sub_id"));
Assert.AreEqual(
"SELECT (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])) AS \"sub_id\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestOrderBySqlSupportsNullOrderingAndRawFragments()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => u.Col(x => x.Id))
.OrderBySql(
u => u.Col(x => x.Code).Asc().NullsLast(),
u => Sql.RawOrder("2 DESC"));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u ORDER BY u.\"user_code\" ASC NULLS LAST, 2 DESC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereSqlSupportsTypedExistsHelper()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => Sql.Exists<TypedFluentUser>(
Database,
sq => sq
.SelectSql(x => x.Col(a => a.Id))
.WhereSql(x => x.Col(a => a.Code).Eq("A")),
"x"))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestInsertSqlObjectProjection()
{
var sq = Database.From(x => x.sample_users.As("x"))
.Select(x => x.x.user_code)
.Where(x => x.x.id_user == 1);
var cmd = Database.InsertTyped<Users>()
.InsertSql(u => new
{
Code = Sql.SubQuery<string>(sq),
First = Sql.Upper(Sql.Val("typed"))
});
Assert.AreEqual(
"INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ((SELECT x.\"user_code\" FROM \"sample_users\" AS x WHERE (x.\"id_user\" = [$0])), UPPER([$1]))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestUpdateSqlObjectProjection()
{
var sq = Database.From(x => x.sample_users.As("x"))
.Select(x => x.x.user_code)
.Where(x => x.x.id_user == 1);
var cmd = Database.UpdateTyped<Users>()
.SetSql(u => new
{
Code = Sql.SubQuery<string>(sq),
First = Sql.Lower(Sql.Val("TYPED"))
})
.WhereSql(u => u.Col(x => x.Id).Eq(1));
Assert.AreEqual(
"UPDATE \"sample_users\" SET \"code\" = (SELECT x.\"user_code\" FROM \"sample_users\" AS x WHERE (x.\"id_user\" = [$0])), \"first\" = LOWER([$1]) WHERE (\"id\" = [$2])",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestSelectSqlSupportsPositionalJoinedContexts()
{
var cmd = Database.FromTyped<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.SelectSql<TypedJoinUserClient>(
(u, c) => u.Col(x => x.IdUser).As("user_id"),
(u, c) => c.Col(x => x.Users).As("client_users"));
Assert.AreEqual(
"SELECT u.\"Id_User\" AS \"user_id\", c.\"Users\" AS \"client_users\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\")",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestWhereGroupHavingAndOrderBySupportPositionalJoinedContexts()
{
var cmd = Database.FromTyped<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Inner().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.SelectSql<TypedJoinUserClient>(
(u, c) => u.Col(x => x.IdUser),
(u, c) => Sql.Count(c.Col(x => x.UserId)).As("cnt"))
.WhereSql<TypedJoinUserClient>((u, c) => u.Col(x => x.Active).Eq(1).And(c.Col(x => x.Deleted).Eq(0)))
.GroupBySql<TypedJoinUserClient>(
(u, c) => u.Col(x => x.IdUser),
(u, c) => c.Col(x => x.Users))
.HavingSql<TypedJoinUserClient>((u, c) => Sql.Count(c.Col(x => x.UserId)).Gt(0))
.OrderBySql<TypedJoinUserClient>(
(u, c) => c.Col(x => x.Users).Asc(),
(u, c) => Sql.Count(c.Col(x => x.UserId)).Desc());
Assert.AreEqual(
"SELECT u.\"Id_User\", COUNT(c.\"User_Id\") AS \"cnt\" FROM \"dbo\".\"Users\" AS u INNER JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") WHERE ((u.\"Active\" = [$0]) AND (c.\"Deleted\" = [$1])) GROUP BY u.\"Id_User\", c.\"Users\" HAVING (COUNT(c.\"User_Id\") > [$2]) ORDER BY c.\"Users\" ASC, COUNT(c.\"User_Id\") DESC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestPositionalJoinedContextsSupportThreeJoins()
{
var cmd = Database.FromTyped<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.Join<TypedJoinUserRole>(j => j.Left().As("r").OnSql((u, r) => u.Col(x => x.IdUser).Eq(r.Col(x => x.UserId))))
.Join<TypedJoinUserRegion>(j => j.Left().As("g").OnSql((u, g) => u.Col(x => x.IdUser).Eq(g.Col(x => x.UserId))))
.SelectSql<TypedJoinUserClient, TypedJoinUserRole, TypedJoinUserRegion>(
(u, c, r, g) => u.Col(x => x.IdUser),
(u, c, r, g) => c.Col(x => x.Users).As("client_users"),
(u, c, r, g) => r.Col(x => x.RoleName).As("role_name"),
(u, c, r, g) => g.Col(x => x.RegionCode).As("region_code"))
.WhereSql<TypedJoinUserClient, TypedJoinUserRole, TypedJoinUserRegion>(
(u, c, r, g) => c.Col(x => x.Deleted).Eq(0).And(r.Col(x => x.RoleId).Gt(0)).And(g.Col(x => x.RegionId).Gt(0)))
.OrderBySql<TypedJoinUserClient, TypedJoinUserRole, TypedJoinUserRegion>(
(u, c, r, g) => r.Col(x => x.RoleName).Asc(),
(u, c, r, g) => g.Col(x => x.RegionCode).Desc());
Assert.AreEqual(
"SELECT u.\"Id_User\", c.\"Users\" AS \"client_users\", r.\"Role_Name\" AS \"role_name\", g.\"Region_Code\" AS \"region_code\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (u.\"Id_User\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (u.\"Id_User\" = g.\"User_Id\") WHERE (((c.\"Deleted\" = [$0]) AND (r.\"Role_Id\" > [$1])) AND (g.\"Region_Id\" > [$2])) ORDER BY r.\"Role_Name\" ASC, g.\"Region_Code\" DESC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestPositionalJoinedContextsSupportFourJoinedTables()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.Join<TypedFluentUser>(j => j.Left().As("b").OnSql((u, b) => u.Col(x => x.Id).Eq(b.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.Id).Eq(c.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("d").OnSql((u, d) => u.Col(x => x.Id).Eq(d.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("e").OnSql((u, e) => u.Col(x => x.Id).Eq(e.Col(x => x.Id))))
.SelectSql<TypedFluentUser, TypedFluentUser, TypedFluentUser, TypedFluentUser>(
(u, b, c, d, e) => u.Col(x => x.Id).As("root_id"),
(u, b, c, d, e) => b.Col(x => x.Code).As("b_code"),
(u, b, c, d, e) => c.Col(x => x.Code).As("c_code"),
(u, b, c, d, e) => d.Col(x => x.Code).As("d_code"),
(u, b, c, d, e) => e.Col(x => x.Code).As("e_code"))
.WhereSql<TypedFluentUser, TypedFluentUser, TypedFluentUser, TypedFluentUser>(
(u, b, c, d, e) => b.Col(x => x.Code).IsNotNull()
.And(c.Col(x => x.Code).IsNotNull())
.And(d.Col(x => x.Code).IsNotNull())
.And(e.Col(x => x.Code).IsNotNull()));
Assert.AreEqual(
"SELECT u.\"id_user\" AS \"root_id\", b.\"user_code\" AS \"b_code\", c.\"user_code\" AS \"c_code\", d.\"user_code\" AS \"d_code\", e.\"user_code\" AS \"e_code\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS b ON (u.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (u.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (u.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (u.\"id_user\" = e.\"id_user\") WHERE ((((b.\"user_code\" IS NOT NULL) AND (c.\"user_code\" IS NOT NULL)) AND (d.\"user_code\" IS NOT NULL)) AND (e.\"user_code\" IS NOT NULL))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestPositionalJoinedContextsValidateRequestedType()
{
var builder = Database.FromTyped<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))));
var ex = Assert.Throws<System.InvalidOperationException>(() =>
builder.SelectSql<TypedJoinUserRole>((u, r) => r.Col(x => x.RoleId).As("role_id")));
Assert.AreEqual(
"Typed join context at position 1 is DynamORM.Tests.Helpers.TypedJoinUserClient, not DynamORM.Tests.Helpers.TypedJoinUserRole.",
ex.Message);
}
[Test]
public void TestPositionalJoinedContextsValidateRequestedPosition()
{
var builder = Database.FromTyped<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))));
var ex = Assert.Throws<System.InvalidOperationException>(() =>
builder.SelectSql<TypedJoinUserClient, TypedJoinUserRole>((u, c, r) => r.Col(x => x.RoleId).As("role_id")));
Assert.AreEqual(
"Typed join context at position 2 is not available.",
ex.Message);
}
}
}

View File

@@ -0,0 +1,214 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System.Text.RegularExpressions;
using DynamORM.Tests.Helpers;
using DynamORM.TypedSql;
using NUnit.Framework;
namespace DynamORM.Tests.TypedSql
{
[TestFixture]
public class TypedSqlScopeDslTests : TestsBase
{
private static string NormalizeSql(string sql)
{
int index = 0;
return Regex.Replace(sql, @"\[\$[^\]]+\]", m => string.Format("[${0}]", index++));
}
[SetUp]
public void SetUp()
{
CreateTestDatabase();
CreateDynamicDatabase(
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset |
DynamicDatabaseOptions.SupportNoLock);
}
[TearDown]
public void TearDown()
{
DestroyDynamicDatabase();
DestroyTestDatabase();
}
[Test]
public void TestScopeBuilderSupportsTwoTableSelectAndWhere()
{
var cmd = Database.FromTypedScope<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.SelectSql(
(u, c) => u.Col(x => x.IdUser).As("user_id"),
(u, c) => c.Col(x => x.Users).As("users"))
.WhereSql((u, c) => u.Col(x => x.Active).Eq(1).And(c.Col(x => x.Deleted).Eq(0)));
Assert.AreEqual(
"SELECT u.\"Id_User\" AS \"user_id\", c.\"Users\" AS \"users\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") WHERE ((u.\"Active\" = [$0]) AND (c.\"Deleted\" = [$1]))",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeBuilderSupportsJoinOnPreviousJoinedAlias()
{
var cmd = Database.FromTypedScope<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.Join<TypedJoinUserRole>(j => j.Left().As("r").OnSql((u, c, r) => c.Col(x => x.UserId).Eq(r.Col(x => x.UserId))))
.SelectSql(
(u, c, r) => u.Col(x => x.IdUser),
(u, c, r) => c.Col(x => x.Users).As("users"),
(u, c, r) => r.Col(x => x.RoleName).As("role_name"))
.OrderBySql(
(u, c, r) => c.Col(x => x.Users).Asc(),
(u, c, r) => r.Col(x => x.RoleName).Desc());
Assert.AreEqual(
"SELECT u.\"Id_User\", c.\"Users\" AS \"users\", r.\"Role_Name\" AS \"role_name\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\") ORDER BY c.\"Users\" ASC, r.\"Role_Name\" DESC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeBuilderSupportsCustomJoinTypeAndNoLock()
{
var cmd = Database.FromTypedScope<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Type("CROSS APPLY").As("c").NoLock())
.SelectSql(
(u, c) => u.Col(x => x.IdUser),
(u, c) => c.All());
Assert.AreEqual(
"SELECT u.\"Id_User\", c.* FROM \"dbo\".\"Users\" AS u CROSS APPLY \"dbo\".\"UserClients\" AS c WITH(NOLOCK)",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeBuilderSupportsHavingAcrossFourJoinedTables()
{
var cmd = Database.FromTypedScope<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.Join<TypedJoinUserRole>(j => j.Left().As("r").OnSql((u, c, r) => u.Col(x => x.IdUser).Eq(r.Col(x => x.UserId))))
.Join<TypedJoinUserRegion>(j => j.Left().As("g").OnSql((u, c, r, g) => r.Col(x => x.UserId).Eq(g.Col(x => x.UserId))))
.SelectSql(
(u, c, r, g) => u.Col(x => x.IdUser),
(u, c, r, g) => Sql.Count(g.Col(x => x.RegionId)).As("region_count"))
.GroupBySql((u, c, r, g) => u.Col(x => x.IdUser))
.HavingSql((u, c, r, g) => Sql.Count(g.Col(x => x.RegionId)).Gt(0));
Assert.AreEqual(
"SELECT u.\"Id_User\", COUNT(g.\"Region_Id\") AS \"region_count\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (u.\"Id_User\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (r.\"User_Id\" = g.\"User_Id\") GROUP BY u.\"Id_User\" HAVING (COUNT(g.\"Region_Id\") > [$0])",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeBuilderSupportsFiveTypedTables()
{
var cmd = Database.FromTypedScope<TypedJoinUser>("u")
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId))))
.Join<TypedJoinUserRole>(j => j.Left().As("r").OnSql((u, c, r) => c.Col(x => x.UserId).Eq(r.Col(x => x.UserId))))
.Join<TypedJoinUserRegion>(j => j.Left().As("g").OnSql((u, c, r, g) => r.Col(x => x.UserId).Eq(g.Col(x => x.UserId))))
.Join<TypedJoinUserDepartment>(j => j.Left().As("d").OnSql((u, c, r, g, d) => g.Col(x => x.RegionId).Eq(d.Col(x => x.RegionId))))
.SelectSql(
(u, c, r, g, d) => u.Col(x => x.IdUser).As("user_id"),
(u, c, r, g, d) => d.Col(x => x.DepartmentName).As("department_name"))
.WhereSql((u, c, r, g, d) => d.Col(x => x.DepartmentId).Gt(0))
.OrderBySql((u, c, r, g, d) => d.Col(x => x.DepartmentName).Asc());
Assert.AreEqual(
"SELECT u.\"Id_User\" AS \"user_id\", d.\"Department_Name\" AS \"department_name\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (r.\"User_Id\" = g.\"User_Id\") LEFT JOIN \"dbo\".\"UserDepartments\" AS d ON (g.\"Region_Id\" = d.\"Region_Id\") WHERE (d.\"Department_Id\" > [$0]) ORDER BY d.\"Department_Name\" ASC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeBuilderSupportsTenTypedTables()
{
var cmd = Database.FromTypedScope<TypedFluentUser>("a")
.Join<TypedFluentUser>(j => j.Left().As("b").OnSql((a, b) => a.Col(x => x.Id).Eq(b.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("c").OnSql((a, b, c) => b.Col(x => x.Id).Eq(c.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("d").OnSql((a, b, c, d) => c.Col(x => x.Id).Eq(d.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("e").OnSql((a, b, c, d, e) => d.Col(x => x.Id).Eq(e.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("f").OnSql((a, b, c, d, e, f) => e.Col(x => x.Id).Eq(f.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("g").OnSql((a, b, c, d, e, f, g) => f.Col(x => x.Id).Eq(g.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("h").OnSql((a, b, c, d, e, f, g, h) => g.Col(x => x.Id).Eq(h.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("i").OnSql((a, b, c, d, e, f, g, h, i) => h.Col(x => x.Id).Eq(i.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("j").OnSql((a, b, c, d, e, f, g, h, i, j) => i.Col(x => x.Id).Eq(j.Col(x => x.Id))))
.SelectSql(
(a, b, c, d, e, f, g, h, i, j) => a.Col(x => x.Id).As("root_id"),
(a, b, c, d, e, f, g, h, i, j) => j.Col(x => x.Code).As("last_code"))
.WhereSql((a, b, c, d, e, f, g, h, i, j) => j.Col(x => x.Code).IsNotNull())
.OrderBySql((a, b, c, d, e, f, g, h, i, j) => j.Col(x => x.Code).Asc());
Assert.AreEqual(
"SELECT a.\"id_user\" AS \"root_id\", j.\"user_code\" AS \"last_code\" FROM \"sample_users\" AS a LEFT JOIN \"sample_users\" AS b ON (a.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (b.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (c.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (d.\"id_user\" = e.\"id_user\") LEFT JOIN \"sample_users\" AS f ON (e.\"id_user\" = f.\"id_user\") LEFT JOIN \"sample_users\" AS g ON (f.\"id_user\" = g.\"id_user\") LEFT JOIN \"sample_users\" AS h ON (g.\"id_user\" = h.\"id_user\") LEFT JOIN \"sample_users\" AS i ON (h.\"id_user\" = i.\"id_user\") LEFT JOIN \"sample_users\" AS j ON (i.\"id_user\" = j.\"id_user\") WHERE (j.\"user_code\" IS NOT NULL) ORDER BY j.\"user_code\" ASC",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeBuilderSupportsSixteenTypedTables()
{
var cmd = Database.FromTypedScope<TypedFluentUser>("a")
.Join<TypedFluentUser>(j => j.Left().As("b").OnSql((a, b) => a.Col(x => x.Id).Eq(b.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("c").OnSql((a, b, c) => b.Col(x => x.Id).Eq(c.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("d").OnSql((a, b, c, d) => c.Col(x => x.Id).Eq(d.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("e").OnSql((a, b, c, d, e) => d.Col(x => x.Id).Eq(e.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("f").OnSql((a, b, c, d, e, f) => e.Col(x => x.Id).Eq(f.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("g").OnSql((a, b, c, d, e, f, g) => f.Col(x => x.Id).Eq(g.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("h").OnSql((a, b, c, d, e, f, g, h) => g.Col(x => x.Id).Eq(h.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("i").OnSql((a, b, c, d, e, f, g, h, i) => h.Col(x => x.Id).Eq(i.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("j").OnSql((a, b, c, d, e, f, g, h, i, j) => i.Col(x => x.Id).Eq(j.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("k").OnSql((a, b, c, d, e, f, g, h, i, j, k) => j.Col(x => x.Id).Eq(k.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("l").OnSql((a, b, c, d, e, f, g, h, i, j, k, l) => k.Col(x => x.Id).Eq(l.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("m").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m) => l.Col(x => x.Id).Eq(m.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("n").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => m.Col(x => x.Id).Eq(n.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("o").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => n.Col(x => x.Id).Eq(o.Col(x => x.Id))))
.Join<TypedFluentUser>(j => j.Left().As("p").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => o.Col(x => x.Id).Eq(p.Col(x => x.Id))))
.SelectSql(
(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => a.Col(x => x.Id).As("root_id"),
(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => p.Col(x => x.Code).As("tail_code"))
.WhereSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => p.Col(x => x.Code).IsNotNull());
Assert.AreEqual(
"SELECT a.\"id_user\" AS \"root_id\", p.\"user_code\" AS \"tail_code\" FROM \"sample_users\" AS a LEFT JOIN \"sample_users\" AS b ON (a.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (b.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (c.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (d.\"id_user\" = e.\"id_user\") LEFT JOIN \"sample_users\" AS f ON (e.\"id_user\" = f.\"id_user\") LEFT JOIN \"sample_users\" AS g ON (f.\"id_user\" = g.\"id_user\") LEFT JOIN \"sample_users\" AS h ON (g.\"id_user\" = h.\"id_user\") LEFT JOIN \"sample_users\" AS i ON (h.\"id_user\" = i.\"id_user\") LEFT JOIN \"sample_users\" AS j ON (i.\"id_user\" = j.\"id_user\") LEFT JOIN \"sample_users\" AS k ON (j.\"id_user\" = k.\"id_user\") LEFT JOIN \"sample_users\" AS l ON (k.\"id_user\" = l.\"id_user\") LEFT JOIN \"sample_users\" AS m ON (l.\"id_user\" = m.\"id_user\") LEFT JOIN \"sample_users\" AS n ON (m.\"id_user\" = n.\"id_user\") LEFT JOIN \"sample_users\" AS o ON (n.\"id_user\" = o.\"id_user\") LEFT JOIN \"sample_users\" AS p ON (o.\"id_user\" = p.\"id_user\") WHERE (p.\"user_code\" IS NOT NULL)",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeSubQueryHelperSupportsScopedJoinChain()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.SelectSql(u => Sql.SubQueryScope<TypedJoinUser, string>(
Database,
sq => sq
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((x, c) => x.Col(a => a.IdUser).Eq(c.Col(a => a.UserId))))
.Join<TypedJoinUserRole>(j => j.Left().As("r").OnSql((x, c, r) => c.Col(a => a.UserId).Eq(r.Col(a => a.UserId))))
.SelectSql((x, c, r) => r.Col(a => a.RoleName)),
"x").As("role_name"));
Assert.AreEqual(
"SELECT (SELECT r.\"Role_Name\" FROM \"dbo\".\"Users\" AS x LEFT JOIN \"dbo\".\"UserClients\" AS c ON (x.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\")) AS \"role_name\" FROM \"sample_users\" AS u",
NormalizeSql(cmd.CommandText()));
}
[Test]
public void TestScopeExistsHelperSupportsScopedJoinChain()
{
var cmd = Database.FromTyped<TypedFluentUser>("u")
.WhereSql(u => Sql.ExistsScope<TypedJoinUser>(
Database,
sq => sq
.Join<TypedJoinUserClient>(j => j.Left().As("c").OnSql((x, c) => x.Col(a => a.IdUser).Eq(c.Col(a => a.UserId))))
.Join<TypedJoinUserRole>(j => j.Left().As("r").OnSql((x, c, r) => c.Col(a => a.UserId).Eq(r.Col(a => a.UserId))))
.SelectSql((x, c, r) => r.Col(a => a.RoleId)),
"x"))
.SelectSql(u => u.Col(x => x.Id));
Assert.AreEqual(
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT r.\"Role_Id\" FROM \"dbo\".\"Users\" AS x LEFT JOIN \"dbo\".\"UserClients\" AS c ON (x.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\")))",
NormalizeSql(cmd.CommandText()));
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
namespace DynamORM.Builders
{
/// <summary>Typed join kind used by typed fluent builder APIs.</summary>
public enum DynamicJoinType
{
Inner = 0,
Join,
Left,
Right,
Full,
LeftOuter,
RightOuter,
FullOuter
}
}

View File

@@ -0,0 +1,25 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Linq.Expressions;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
/// <summary>Typed delete query builder for mapped entities.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
public interface IDynamicTypedDeleteQueryBuilder<T> : IDynamicDeleteQueryBuilder
{
/// <summary>Add typed where predicate using mapped properties.</summary>
/// <param name="predicate">Predicate to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedDeleteQueryBuilder<T> Where(Expression<Func<T, bool>> predicate);
/// <summary>Add typed SQL DSL where predicate.</summary>
IDynamicTypedDeleteQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate);
}
}

View File

@@ -0,0 +1,35 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Linq.Expressions;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
/// <summary>Typed insert query builder for mapped entities.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
public interface IDynamicTypedInsertQueryBuilder<T> : IDynamicInsertQueryBuilder
{
/// <summary>Add typed insert assignment using mapped property selector.</summary>
/// <typeparam name="TValue">Property type.</typeparam>
/// <param name="selector">Property selector.</param>
/// <param name="value">Value to insert.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedInsertQueryBuilder<T> Insert<TValue>(Expression<Func<T, TValue>> selector, object value);
/// <summary>Add values from mapped object.</summary>
/// <param name="value">Mapped object value.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedInsertQueryBuilder<T> Insert(T value);
/// <summary>Add typed SQL DSL insert assignment.</summary>
IDynamicTypedInsertQueryBuilder<T> InsertSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory);
/// <summary>Add typed SQL DSL insert assignments from object projection.</summary>
IDynamicTypedInsertQueryBuilder<T> InsertSql(Func<TypedTableContext<T>, object> values);
}
}

View File

@@ -0,0 +1,169 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, 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.Linq.Expressions;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
/// <summary>Typed select query builder for mapped entities.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
public interface IDynamicTypedSelectQueryBuilder<T> : IDynamicSelectQueryBuilder
{
/// <summary>Add typed join to mapped table.</summary>
/// <typeparam name="TRight">Joined mapped entity type.</typeparam>
/// <param name="on">Join ON predicate.</param>
/// <param name="alias">Optional alias for joined table.</param>
/// <param name="joinType">Join type.</param>
/// <param name="noLock">Adds NOLOCK hint to joined source when supported by provider options.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> Join<TRight>(Expression<Func<T, TRight, bool>> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false);
/// <summary>Add typed join with custom join type text (for example: CROSS APPLY).</summary>
/// <typeparam name="TRight">Joined mapped entity type.</typeparam>
/// <param name="on">Optional join ON predicate.</param>
/// <param name="alias">Optional alias for joined table.</param>
/// <param name="joinType">Join type text.</param>
/// <param name="noLock">Adds NOLOCK hint to joined source when supported by provider options.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> Join<TRight>(Expression<Func<T, TRight, bool>> on, string alias, string joinType, bool noLock = false);
/// <summary>Add typed join using join-spec builder syntax (<c>As()</c>, join kind and <c>On()</c>).</summary>
/// <typeparam name="TRight">Joined mapped entity type.</typeparam>
/// <param name="specification">Join specification builder callback.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> Join<TRight>(Func<TypedJoinBuilder<T, TRight>, TypedJoinBuilder<T, TRight>> specification);
/// <summary>Add typed where predicate using mapped properties.</summary>
/// <param name="predicate">Predicate to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> Where(Expression<Func<T, bool>> predicate);
/// <summary>Add typed having predicate using mapped properties.</summary>
/// <param name="predicate">Predicate to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> Having(Expression<Func<T, bool>> predicate);
/// <summary>Add typed selected columns using mapped properties.</summary>
/// <typeparam name="TResult">Projection type.</typeparam>
/// <param name="selector">Selector to parse.</param>
/// <param name="selectors">Additional selectors to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> Select<TResult>(Expression<Func<T, TResult>> selector, params Expression<Func<T, TResult>>[] selectors);
/// <summary>Add typed group by columns using mapped properties.</summary>
/// <typeparam name="TResult">Projection type.</typeparam>
/// <param name="selector">Selector to parse.</param>
/// <param name="selectors">Additional selectors to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> GroupBy<TResult>(Expression<Func<T, TResult>> selector, params Expression<Func<T, TResult>>[] selectors);
/// <summary>Add typed order by columns using mapped properties. Supports <c>Asc()</c>/<c>Desc()</c>.</summary>
/// <typeparam name="TResult">Projection type.</typeparam>
/// <param name="selector">Selector to parse.</param>
/// <param name="selectors">Additional selectors to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedSelectQueryBuilder<T> OrderBy<TResult>(Expression<Func<T, TResult>> selector, params Expression<Func<T, TResult>>[] selectors);
/// <summary>Add typed SQL DSL select items.</summary>
IDynamicTypedSelectQueryBuilder<T> SelectSql(Func<TypedTableContext<T>, TypedSqlSelectable> selector, params Func<TypedTableContext<T>, TypedSqlSelectable>[] selectors);
/// <summary>Add typed SQL DSL where predicate.</summary>
IDynamicTypedSelectQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL having predicate.</summary>
IDynamicTypedSelectQueryBuilder<T> HavingSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL group by expressions.</summary>
IDynamicTypedSelectQueryBuilder<T> GroupBySql(Func<TypedTableContext<T>, TypedSqlExpression> selector, params Func<TypedTableContext<T>, TypedSqlExpression>[] selectors);
/// <summary>Add typed SQL DSL order by expressions.</summary>
IDynamicTypedSelectQueryBuilder<T> OrderBySql(Func<TypedTableContext<T>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T>, TypedSqlOrderExpression>[] selectors);
/// <summary>Add typed SQL DSL select items using root and first joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> SelectSql<TJoin1>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlSelectable> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlSelectable>[] selectors);
/// <summary>Add typed SQL DSL select items using root and first two joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> SelectSql<TJoin1, TJoin2>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlSelectable> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlSelectable>[] selectors);
/// <summary>Add typed SQL DSL select items using root and first three joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> SelectSql<TJoin1, TJoin2, TJoin3>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlSelectable> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlSelectable>[] selectors);
/// <summary>Add typed SQL DSL select items using root and first four joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> SelectSql<TJoin1, TJoin2, TJoin3, TJoin4>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlSelectable> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlSelectable>[] selectors);
/// <summary>Add typed SQL DSL where predicate using root and first joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> WhereSql<TJoin1>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL where predicate using root and first two joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> WhereSql<TJoin1, TJoin2>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL where predicate using root and first three joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> WhereSql<TJoin1, TJoin2, TJoin3>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL where predicate using root and first four joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> WhereSql<TJoin1, TJoin2, TJoin3, TJoin4>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL having predicate using root and first joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> HavingSql<TJoin1>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL having predicate using root and first two joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> HavingSql<TJoin1, TJoin2>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL having predicate using root and first three joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> HavingSql<TJoin1, TJoin2, TJoin3>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL having predicate using root and first four joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> HavingSql<TJoin1, TJoin2, TJoin3, TJoin4>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL group-by expression using root and first joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> GroupBySql<TJoin1>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlExpression>[] selectors);
/// <summary>Add typed SQL DSL group-by expression using root and first two joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> GroupBySql<TJoin1, TJoin2>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlExpression>[] selectors);
/// <summary>Add typed SQL DSL group-by expression using root and first three joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> GroupBySql<TJoin1, TJoin2, TJoin3>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlExpression>[] selectors);
/// <summary>Add typed SQL DSL group-by expression using root and first four joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> GroupBySql<TJoin1, TJoin2, TJoin3, TJoin4>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlExpression>[] selectors);
/// <summary>Add typed SQL DSL order-by expression using root and first joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> OrderBySql<TJoin1>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedSqlOrderExpression>[] selectors);
/// <summary>Add typed SQL DSL order-by expression using root and first two joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> OrderBySql<TJoin1, TJoin2>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedSqlOrderExpression>[] selectors);
/// <summary>Add typed SQL DSL order-by expression using root and first three joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> OrderBySql<TJoin1, TJoin2, TJoin3>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedSqlOrderExpression>[] selectors);
/// <summary>Add typed SQL DSL order-by expression using root and first four joined table contexts.</summary>
IDynamicTypedSelectQueryBuilder<T> OrderBySql<TJoin1, TJoin2, TJoin3, TJoin4>(Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T>, TypedTableContext<TJoin1>, TypedTableContext<TJoin2>, TypedTableContext<TJoin3>, TypedTableContext<TJoin4>, TypedSqlOrderExpression>[] selectors);
}
}

View File

@@ -0,0 +1,171 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
public interface IDynamicTypedSelectScopeQueryBuilder<T1> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, TJoin2> Join<TJoin2>(Func<TypedScopeJoinBuilder<T1, TJoin2>, TypedScopeJoinBuilder<T1, TJoin2>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1> SelectSql(Func<TypedTableContext<T1>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1> WhereSql(Func<TypedTableContext<T1>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1> HavingSql(Func<TypedTableContext<T1>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1> GroupBySql(Func<TypedTableContext<T1>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1> OrderBySql(Func<TypedTableContext<T1>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, TJoin3> Join<TJoin3>(Func<TypedScopeJoinBuilder<T1, T2, TJoin3>, TypedScopeJoinBuilder<T1, T2, TJoin3>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, TJoin4> Join<TJoin4>(Func<TypedScopeJoinBuilder<T1, T2, T3, TJoin4>, TypedScopeJoinBuilder<T1, T2, T3, TJoin4>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, TJoin5> Join<TJoin5>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, TJoin5>, TypedScopeJoinBuilder<T1, T2, T3, T4, TJoin5>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, TJoin6> Join<TJoin6>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, TJoin6>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, TJoin6>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, TJoin7> Join<TJoin7>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, TJoin7>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, TJoin7>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, TJoin8> Join<TJoin8>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, TJoin8>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, TJoin8>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TJoin9> Join<TJoin9>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TJoin9>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TJoin9>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TJoin10> Join<TJoin10>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TJoin10>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TJoin10>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TJoin11> Join<TJoin11>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TJoin11>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TJoin11>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TJoin12> Join<TJoin12>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TJoin12>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TJoin12>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TJoin13> Join<TJoin13>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TJoin13>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TJoin13>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TJoin14> Join<TJoin14>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TJoin14>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TJoin14>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TJoin15> Join<TJoin15>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TJoin15>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TJoin15>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TJoin16> Join<TJoin16>(Func<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TJoin16>, TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TJoin16>> specification);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedSqlOrderExpression>[] selectors);
}
public interface IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : IDynamicSelectQueryBuilder
{
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> SelectSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlSelectable> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlSelectable>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> WhereSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> HavingSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlPredicate> predicate);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> GroupBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlExpression>[] selectors);
IDynamicTypedSelectScopeQueryBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> OrderBySql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlOrderExpression> selector, params Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<T16>, TypedSqlOrderExpression>[] selectors);
}
}

View File

@@ -0,0 +1,43 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Linq.Expressions;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
/// <summary>Typed update query builder for mapped entities.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
public interface IDynamicTypedUpdateQueryBuilder<T> : IDynamicUpdateQueryBuilder
{
/// <summary>Add typed where predicate using mapped properties.</summary>
/// <param name="predicate">Predicate to parse.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedUpdateQueryBuilder<T> Where(Expression<Func<T, bool>> predicate);
/// <summary>Add typed assignment using mapped property selector.</summary>
/// <typeparam name="TValue">Property type.</typeparam>
/// <param name="selector">Property selector.</param>
/// <param name="value">Assigned value.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedUpdateQueryBuilder<T> Set<TValue>(Expression<Func<T, TValue>> selector, object value);
/// <summary>Add update values from mapped object.</summary>
/// <param name="value">Mapped object value.</param>
/// <returns>Builder instance.</returns>
IDynamicTypedUpdateQueryBuilder<T> Values(T value);
/// <summary>Add typed SQL DSL where predicate.</summary>
IDynamicTypedUpdateQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate);
/// <summary>Add typed SQL DSL assignment.</summary>
IDynamicTypedUpdateQueryBuilder<T> SetSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory);
/// <summary>Add typed SQL DSL assignments from object projection.</summary>
IDynamicTypedUpdateQueryBuilder<T> SetSql(Func<TypedTableContext<T>, object> values);
}
}

View File

@@ -40,8 +40,8 @@ namespace DynamORM.Builders.Implementation
/// <summary>Implementation of dynamic insert query builder.</summary>
internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder
{
private string _columns;
private string _values;
protected string _columns;
protected string _values;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.

View File

@@ -48,11 +48,11 @@ namespace DynamORM.Builders.Implementation
private int? _offset = null;
private bool _distinct = false;
private string _select;
protected string _select;
private string _from;
private string _join;
private string _groupby;
private string _orderby;
protected string _join;
protected string _groupby;
protected string _orderby;
#region IQueryWithHaving

View File

@@ -0,0 +1,98 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using DynamORM.Builders.Extensions;
using DynamORM.TypedSql;
namespace DynamORM.Builders.Implementation
{
/// <summary>Typed wrapper over <see cref="DynamicDeleteQueryBuilder"/> with property-to-column translation.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
internal class DynamicTypedDeleteQueryBuilder<T> : DynamicDeleteQueryBuilder, IDynamicTypedDeleteQueryBuilder<T>
{
internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db)
: this(db, false)
{
}
internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db, bool mapType)
: base(db)
{
if (mapType)
this.Table(typeof(T));
else
this.Table(typeof(T).Name);
}
public IDynamicTypedDeleteQueryBuilder<T> Where(Expression<Func<T, bool>> predicate)
{
TypedModifyHelper.ApplyWhere<T>((c, o, v) => base.Where(c, o, v), predicate);
return this;
}
public IDynamicTypedDeleteQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate)
{
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery);
if (string.IsNullOrEmpty(WhereCondition))
WhereCondition = condition;
else
WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition);
return this;
}
public new IDynamicTypedDeleteQueryBuilder<T> Where(Func<dynamic, object> func)
{
base.Where(func);
return this;
}
public new IDynamicTypedDeleteQueryBuilder<T> Where(DynamicColumn column)
{
base.Where(column);
return this;
}
public new IDynamicTypedDeleteQueryBuilder<T> Where(string column, DynamicColumn.CompareOperator op, object value)
{
base.Where(column, op, value);
return this;
}
public new IDynamicTypedDeleteQueryBuilder<T> Where(string column, object value)
{
base.Where(column, value);
return this;
}
public new IDynamicTypedDeleteQueryBuilder<T> Where(object conditions, bool schema = false)
{
base.Where(conditions, schema);
return this;
}
private string RenderValue(object value)
{
if (value == null)
return "NULL";
DynamicSchemaColumn? columnSchema = null;
return ParseConstant(value, Parameters, columnSchema);
}
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
{
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
if (!Parameters.ContainsKey(item.Key))
Parameters.Add(item.Key, item.Value);
return query.CommandText();
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using DynamORM.Builders.Extensions;
using DynamORM.Helpers.Dynamics;
using DynamORM.TypedSql;
namespace DynamORM.Builders.Implementation
{
/// <summary>Typed wrapper over <see cref="DynamicInsertQueryBuilder"/> with property-to-column translation.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
internal class DynamicTypedInsertQueryBuilder<T> : DynamicInsertQueryBuilder, IDynamicTypedInsertQueryBuilder<T>
{
internal DynamicTypedInsertQueryBuilder(DynamicDatabase db)
: this(db, false)
{
}
internal DynamicTypedInsertQueryBuilder(DynamicDatabase db, bool mapType)
: base(db)
{
if (mapType)
this.Table(typeof(T));
else
this.Table(typeof(T).Name);
}
public IDynamicTypedInsertQueryBuilder<T> Insert<TValue>(Expression<Func<T, TValue>> selector, object value)
{
base.Insert(TypedModifyHelper.GetMappedColumn(selector), value);
return this;
}
public IDynamicTypedInsertQueryBuilder<T> Insert(T value)
{
base.Insert(value);
return this;
}
public IDynamicTypedInsertQueryBuilder<T> InsertSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory)
{
string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery);
_columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
return this;
}
public IDynamicTypedInsertQueryBuilder<T> InsertSql(Func<TypedTableContext<T>, object> values)
{
if (values == null)
throw new ArgumentNullException("values");
object data = values(new TypedTableContext<T>(null));
foreach (KeyValuePair<string, object> item in data.ToDictionary())
{
string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true);
string value = (item.Value as TypedSqlExpression) != null
? TypedModifyHelper.RenderExpression<T>(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery)
: RenderValue(item.Value);
_columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
}
return this;
}
public new IDynamicTypedInsertQueryBuilder<T> Values(Func<dynamic, object> fn, params Func<dynamic, object>[] func)
{
base.Values(fn, func);
return this;
}
public new IDynamicTypedInsertQueryBuilder<T> Insert(string column, object value)
{
base.Insert(column, value);
return this;
}
public new IDynamicTypedInsertQueryBuilder<T> Insert(object o)
{
base.Insert(o);
return this;
}
private string RenderValue(object value)
{
if (value == null)
return "NULL";
DynamicSchemaColumn? columnSchema = null;
return ParseConstant(value, Parameters, columnSchema);
}
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
{
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
if (!Parameters.ContainsKey(item.Key))
Parameters.Add(item.Key, item.Value);
return query.CommandText();
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using DynamORM.Builders.Extensions;
using DynamORM.Helpers.Dynamics;
using DynamORM.TypedSql;
namespace DynamORM.Builders.Implementation
{
/// <summary>Typed wrapper over <see cref="DynamicUpdateQueryBuilder"/> with property-to-column translation.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
internal class DynamicTypedUpdateQueryBuilder<T> : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder<T>
{
internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db)
: this(db, false)
{
}
internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db, bool mapType)
: base(db)
{
if (mapType)
this.Table(typeof(T));
else
this.Table(typeof(T).Name);
}
public IDynamicTypedUpdateQueryBuilder<T> Where(Expression<Func<T, bool>> predicate)
{
TypedModifyHelper.ApplyWhere<T>((c, o, v) => base.Where(c, o, v), predicate);
return this;
}
public IDynamicTypedUpdateQueryBuilder<T> Set<TValue>(Expression<Func<T, TValue>> selector, object value)
{
base.Values(TypedModifyHelper.GetMappedColumn(selector), value);
return this;
}
public IDynamicTypedUpdateQueryBuilder<T> Values(T value)
{
base.Values(value);
return this;
}
public IDynamicTypedUpdateQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate)
{
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery);
if (string.IsNullOrEmpty(WhereCondition))
WhereCondition = condition;
else
WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition);
return this;
}
public IDynamicTypedUpdateQueryBuilder<T> SetSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory)
{
string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery);
string assignment = string.Format("{0} = {1}", column, value);
_columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
return this;
}
public IDynamicTypedUpdateQueryBuilder<T> SetSql(Func<TypedTableContext<T>, object> values)
{
if (values == null)
throw new ArgumentNullException("values");
object data = values(new TypedTableContext<T>(null));
foreach (KeyValuePair<string, object> item in data.ToDictionary())
{
string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true);
string value = (item.Value as TypedSqlExpression) != null
? TypedModifyHelper.RenderExpression<T>(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery)
: RenderValue(item.Value);
string assignment = string.Format("{0} = {1}", column, value);
_columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
}
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Update(string column, object value)
{
base.Update(column, value);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Update(object conditions)
{
base.Update(conditions);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Set(params Func<dynamic, object>[] func)
{
base.Set(func);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Values(string column, object value)
{
base.Values(column, value);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Values(object o)
{
base.Values(o);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Where(Func<dynamic, object> func)
{
base.Where(func);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Where(DynamicColumn column)
{
base.Where(column);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Where(string column, DynamicColumn.CompareOperator op, object value)
{
base.Where(column, op, value);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Where(string column, object value)
{
base.Where(column, value);
return this;
}
public new IDynamicTypedUpdateQueryBuilder<T> Where(object conditions, bool schema = false)
{
base.Where(conditions, schema);
return this;
}
private string RenderValue(object value)
{
if (value == null)
return "NULL";
DynamicSchemaColumn? columnSchema = null;
return ParseConstant(value, Parameters, columnSchema);
}
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
{
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
if (!Parameters.ContainsKey(item.Key))
Parameters.Add(item.Key, item.Value);
return query.CommandText();
}
}
}

View File

@@ -41,7 +41,7 @@ namespace DynamORM.Builders.Implementation
/// <summary>Update query builder.</summary>
internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
{
private string _columns;
protected string _columns;
internal DynamicUpdateQueryBuilder(DynamicDatabase db)
: base(db)

View File

@@ -0,0 +1,200 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using DynamORM.Builders;
using DynamORM.Mapper;
using DynamORM.TypedSql;
namespace DynamORM.Builders.Implementation
{
/// <summary>Helper methods for typed modify builders.</summary>
internal static class TypedModifyHelper
{
private sealed class ModifyRenderContext : ITypedSqlRenderContext
{
private readonly Func<Type, string, string, Func<string, string>, string> _resolveColumn;
private readonly Func<object, string> _renderValue;
private readonly Func<string, string> _decorateName;
private readonly Func<IDynamicSelectQueryBuilder, string> _renderSubQuery;
public ModifyRenderContext(Func<Type, string, string, Func<string, string>, string> resolveColumn, Func<object, string> renderValue, Func<string, string> decorateName, Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
{
_resolveColumn = resolveColumn;
_renderValue = renderValue;
_decorateName = decorateName;
_renderSubQuery = renderSubQuery;
}
public string ResolveColumn(Type modelType, string memberName, string alias)
{
return _resolveColumn(modelType, memberName, alias, _decorateName);
}
public string RenderValue(object value)
{
return _renderValue(value);
}
public string DecorateName(string name)
{
return _decorateName(name);
}
public string RenderSubQuery(IDynamicSelectQueryBuilder query)
{
return _renderSubQuery(query);
}
}
public static string GetMappedColumn<T, TValue>(Expression<Func<T, TValue>> selector)
{
if (selector == null)
throw new ArgumentNullException("selector");
return GetMappedColumn(typeof(T), selector.Body);
}
public static void ApplyWhere<T>(Action<string, DynamicColumn.CompareOperator, object> addCondition, Expression<Func<T, bool>> predicate)
{
if (addCondition == null)
throw new ArgumentNullException("addCondition");
if (predicate == null)
throw new ArgumentNullException("predicate");
ApplyWhereInternal(typeof(T), addCondition, predicate.Body);
}
public static string RenderPredicate<T>(
Func<TypedTableContext<T>, TypedSqlPredicate> predicate,
string alias,
Func<object, string> renderValue,
Func<string, string> decorateName,
Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery);
return predicate(new TypedTableContext<T>(alias)).Render(context);
}
public static string RenderExpression<T>(
Func<TypedTableContext<T>, TypedSqlExpression> expression,
string alias,
Func<object, string> renderValue,
Func<string, string> decorateName,
Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
{
if (expression == null)
throw new ArgumentNullException("expression");
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery);
return expression(new TypedTableContext<T>(alias)).Render(context);
}
private static void ApplyWhereInternal(Type modelType, Action<string, DynamicColumn.CompareOperator, object> addCondition, Expression expression)
{
expression = UnwrapConvert(expression);
BinaryExpression be = expression as BinaryExpression;
if (be == null)
throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression));
if (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)
{
ApplyWhereInternal(modelType, addCondition, be.Left);
ApplyWhereInternal(modelType, addCondition, be.Right);
return;
}
string col = GetMappedColumn(modelType, be.Left);
object val = EvaluateExpression(be.Right);
switch (be.NodeType)
{
case ExpressionType.Equal:
addCondition(col, DynamicColumn.CompareOperator.Eq, val);
return;
case ExpressionType.NotEqual:
addCondition(col, DynamicColumn.CompareOperator.Not, val);
return;
case ExpressionType.GreaterThan:
addCondition(col, DynamicColumn.CompareOperator.Gt, val);
return;
case ExpressionType.GreaterThanOrEqual:
addCondition(col, DynamicColumn.CompareOperator.Gte, val);
return;
case ExpressionType.LessThan:
addCondition(col, DynamicColumn.CompareOperator.Lt, val);
return;
case ExpressionType.LessThanOrEqual:
addCondition(col, DynamicColumn.CompareOperator.Lte, val);
return;
}
throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression));
}
private static string GetMappedColumn(Type modelType, Expression expression)
{
expression = UnwrapConvert(expression);
MemberExpression member = expression as MemberExpression;
if (member == null)
throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression));
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType);
if (mapper == null)
throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName));
return mapper.PropertyMap.TryGetValue(member.Member.Name)
?? mapper.PropertyMap
.Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase))
.Select(x => x.Value)
.FirstOrDefault()
?? member.Member.Name;
}
private static string ResolveColumn(Type modelType, string memberName, string alias, Func<string, string> decorateName)
{
string mapped = GetMappedColumnByName(modelType, memberName);
return string.IsNullOrEmpty(alias)
? decorateName(mapped)
: string.Format("{0}.{1}", alias, decorateName(mapped));
}
internal static string GetMappedColumnByName(Type modelType, string memberName)
{
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType);
if (mapper == null)
throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName));
return mapper.PropertyMap.TryGetValue(memberName)
?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault()
?? memberName;
}
private static Expression UnwrapConvert(Expression expression)
{
while (expression is UnaryExpression &&
(((UnaryExpression)expression).NodeType == ExpressionType.Convert ||
((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked))
expression = ((UnaryExpression)expression).Operand;
return expression;
}
private static object EvaluateExpression(Expression expression)
{
expression = UnwrapConvert(expression);
var objectMember = Expression.Convert(expression, typeof(object));
var getter = Expression.Lambda<Func<object>>(objectMember).Compile();
return getter();
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, 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.Builders
{
/// <summary>Marker extensions for typed fluent builder expressions.</summary>
public static class TypedFluentExtensions
{
/// <summary>Marks select projection alias in typed expressions.</summary>
public static T As<T>(this T source, string alias)
{
return source;
}
/// <summary>Marks ascending order in typed order expressions.</summary>
public static T Asc<T>(this T source)
{
return source;
}
/// <summary>Marks descending order in typed order expressions.</summary>
public static T Desc<T>(this T source)
{
return source;
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Linq.Expressions;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
/// <summary>Typed join specification builder used by typed fluent select queries.</summary>
/// <typeparam name="TLeft">Left side mapped type.</typeparam>
/// <typeparam name="TRight">Right side mapped type.</typeparam>
public class TypedJoinBuilder<TLeft, TRight>
{
internal TypedJoinBuilder()
{
JoinType = DynamicJoinType.Join;
}
/// <summary>Gets join alias.</summary>
public string Alias { get; private set; }
/// <summary>Gets join type.</summary>
public DynamicJoinType JoinType { get; private set; }
/// <summary>Gets custom join type text.</summary>
public string CustomJoinType { get; private set; }
/// <summary>Gets a value indicating whether joined source should use NOLOCK.</summary>
public bool UseNoLock { get; private set; }
/// <summary>Gets ON predicate.</summary>
public Expression<Func<TLeft, TRight, bool>> OnPredicate { get; private set; }
/// <summary>Gets raw ON condition.</summary>
public string OnRawCondition { get; private set; }
/// <summary>Gets typed SQL DSL ON specification.</summary>
public Func<TypedTableContext<TLeft>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
/// <summary>Sets join alias.</summary>
public TypedJoinBuilder<TLeft, TRight> As(string alias)
{
Alias = alias;
return this;
}
/// <summary>Sets INNER JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> Inner()
{
JoinType = DynamicJoinType.Inner;
CustomJoinType = null;
return this;
}
/// <summary>Sets plain JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> Join()
{
JoinType = DynamicJoinType.Join;
CustomJoinType = null;
return this;
}
/// <summary>Sets LEFT JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> Left()
{
JoinType = DynamicJoinType.Left;
CustomJoinType = null;
return this;
}
/// <summary>Sets RIGHT JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> Right()
{
JoinType = DynamicJoinType.Right;
CustomJoinType = null;
return this;
}
/// <summary>Sets FULL JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> Full()
{
JoinType = DynamicJoinType.Full;
CustomJoinType = null;
return this;
}
/// <summary>Sets LEFT OUTER JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> LeftOuter()
{
JoinType = DynamicJoinType.LeftOuter;
CustomJoinType = null;
return this;
}
/// <summary>Sets RIGHT OUTER JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> RightOuter()
{
JoinType = DynamicJoinType.RightOuter;
CustomJoinType = null;
return this;
}
/// <summary>Sets FULL OUTER JOIN.</summary>
public TypedJoinBuilder<TLeft, TRight> FullOuter()
{
JoinType = DynamicJoinType.FullOuter;
CustomJoinType = null;
return this;
}
/// <summary>Sets custom join type text (for example: CROSS APPLY).</summary>
public TypedJoinBuilder<TLeft, TRight> Type(string joinType)
{
if (string.IsNullOrEmpty(joinType))
throw new ArgumentNullException("joinType");
CustomJoinType = joinType.Trim();
return this;
}
/// <summary>Marks joined source with NOLOCK hint.</summary>
public TypedJoinBuilder<TLeft, TRight> NoLock(bool use = true)
{
UseNoLock = use;
return this;
}
/// <summary>Sets ON predicate.</summary>
public TypedJoinBuilder<TLeft, TRight> On(Expression<Func<TLeft, TRight, bool>> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnPredicate = predicate;
OnRawCondition = null;
OnSqlPredicate = null;
return this;
}
/// <summary>Sets raw ON clause (without the ON keyword).</summary>
public TypedJoinBuilder<TLeft, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnPredicate = null;
OnSqlPredicate = null;
return this;
}
/// <summary>Sets typed SQL DSL ON predicate.</summary>
public TypedJoinBuilder<TLeft, TRight> OnSql(Func<TypedTableContext<TLeft>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnPredicate = null;
OnRawCondition = null;
return this;
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Linq.Expressions;
namespace DynamORM.Builders
{
/// <summary>Compatibility and convenience extensions for typed joins.</summary>
public static class TypedJoinExtensions
{
/// <summary>
/// Legacy compatibility helper. Prefer <see cref="IDynamicTypedSelectQueryBuilder{T}.Join{TRight}"/>.
/// </summary>
public static IDynamicTypedSelectQueryBuilder<TLeft> JoinTyped<TLeft, TRight>(
this IDynamicTypedSelectQueryBuilder<TLeft> builder,
string alias,
Expression<Func<TLeft, TRight, bool>> on,
string joinType = "INNER JOIN")
{
if (builder == null) throw new ArgumentNullException("builder");
if (on == null) throw new ArgumentNullException("on");
DynamicJoinType jt = DynamicJoinType.Inner;
string normalized = (joinType ?? string.Empty).Trim().ToUpperInvariant();
if (normalized == "JOIN")
jt = DynamicJoinType.Join;
else if (normalized == "LEFT OUTER JOIN" || normalized == "LEFT OUTER")
jt = DynamicJoinType.LeftOuter;
else if (normalized == "RIGHT OUTER JOIN" || normalized == "RIGHT OUTER")
jt = DynamicJoinType.RightOuter;
else if (normalized == "FULL OUTER JOIN" || normalized == "FULL OUTER")
jt = DynamicJoinType.FullOuter;
else if (normalized == "LEFT JOIN" || normalized == "LEFT")
jt = DynamicJoinType.Left;
else if (normalized == "RIGHT JOIN" || normalized == "RIGHT")
jt = DynamicJoinType.Right;
else if (normalized == "FULL JOIN" || normalized == "FULL")
jt = DynamicJoinType.Full;
else if (normalized != "INNER JOIN" && normalized != "INNER" && normalized != "JOIN")
return builder.Join(on, alias, joinType);
return builder.Join(on, alias, jt);
}
/// <summary>Convenience typed INNER JOIN extension.</summary>
public static IDynamicTypedSelectQueryBuilder<TLeft> InnerJoinTyped<TLeft, TRight>(
this IDynamicTypedSelectQueryBuilder<TLeft> builder,
Expression<Func<TLeft, TRight, bool>> on,
string alias = null)
{
return builder.Join(on, alias, DynamicJoinType.Inner);
}
/// <summary>Convenience typed LEFT JOIN extension.</summary>
public static IDynamicTypedSelectQueryBuilder<TLeft> LeftJoinTyped<TLeft, TRight>(
this IDynamicTypedSelectQueryBuilder<TLeft> builder,
Expression<Func<TLeft, TRight, bool>> on,
string alias = null)
{
return builder.Join(on, alias, DynamicJoinType.Left);
}
/// <summary>Convenience typed RIGHT JOIN extension.</summary>
public static IDynamicTypedSelectQueryBuilder<TLeft> RightJoinTyped<TLeft, TRight>(
this IDynamicTypedSelectQueryBuilder<TLeft> builder,
Expression<Func<TLeft, TRight, bool>> on,
string alias = null)
{
return builder.Join(on, alias, DynamicJoinType.Right);
}
/// <summary>Convenience typed FULL JOIN extension.</summary>
public static IDynamicTypedSelectQueryBuilder<TLeft> FullJoinTyped<TLeft, TRight>(
this IDynamicTypedSelectQueryBuilder<TLeft> builder,
Expression<Func<TLeft, TRight, bool>> on,
string alias = null)
{
return builder.Join(on, alias, DynamicJoinType.Full);
}
}
}

View File

@@ -0,0 +1,398 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using DynamORM.TypedSql;
namespace DynamORM.Builders
{
public abstract class TypedScopeJoinBuilderBase<TSelf>
where TSelf : TypedScopeJoinBuilderBase<TSelf>
{
public string Alias { get; private set; }
public DynamicJoinType JoinType { get; private set; }
public string CustomJoinType { get; private set; }
public bool UseNoLock { get; private set; }
protected TypedScopeJoinBuilderBase()
{
JoinType = DynamicJoinType.Join;
}
public TSelf As(string alias) { Alias = alias; return (TSelf)this; }
public TSelf Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return (TSelf)this; }
public TSelf Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return (TSelf)this; }
public TSelf Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return (TSelf)this; }
public TSelf Right() { JoinType = DynamicJoinType.Right; CustomJoinType = null; return (TSelf)this; }
public TSelf Full() { JoinType = DynamicJoinType.Full; CustomJoinType = null; return (TSelf)this; }
public TSelf LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return (TSelf)this; }
public TSelf RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return (TSelf)this; }
public TSelf FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return (TSelf)this; }
public TSelf Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); CustomJoinType = joinType.Trim(); return (TSelf)this; }
public TSelf NoLock(bool use = true) { UseNoLock = use; return (TSelf)this; }
}
public sealed class TypedScopeJoinBuilder<T1, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
public sealed class TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TRight> : TypedScopeJoinBuilderBase<TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TRight>>
{
internal Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<TRight>, TypedSqlPredicate> OnSqlPredicate { get; private set; }
internal string OnRawCondition { get; private set; }
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TRight> OnSql(Func<TypedTableContext<T1>, TypedTableContext<T2>, TypedTableContext<T3>, TypedTableContext<T4>, TypedTableContext<T5>, TypedTableContext<T6>, TypedTableContext<T7>, TypedTableContext<T8>, TypedTableContext<T9>, TypedTableContext<T10>, TypedTableContext<T11>, TypedTableContext<T12>, TypedTableContext<T13>, TypedTableContext<T14>, TypedTableContext<T15>, TypedTableContext<TRight>, TypedSqlPredicate> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
OnSqlPredicate = predicate;
OnRawCondition = null;
return this;
}
public TypedScopeJoinBuilder<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TRight> OnRaw(string condition)
{
if (string.IsNullOrEmpty(condition))
throw new ArgumentNullException("condition");
OnRawCondition = condition.Trim();
OnSqlPredicate = null;
return this;
}
}
}

View File

@@ -6,7 +6,7 @@
<Copyright>Copyright © RUSSEK Software 2012-2026</Copyright>
<Company>RUSSEK Software</Company>
<Authors>Grzegorz Russek</Authors>
<VersionPrefix>1.9</VersionPrefix>
<VersionPrefix>2.0</VersionPrefix>
<RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl>
<PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl>
<Product>DynamORM</Product>

View File

@@ -472,6 +472,46 @@ namespace DynamORM
}
}
/// <summary>Adds to the <code>FROM</code> clause using <see cref="Type"/>.</summary>
/// <typeparam name="T">Type which can be represented in database.</typeparam>
/// <param name="alias">Table alias.</param>
/// <param name="noLock">use no lock.</param>
/// <returns>This instance to permit chaining.</returns>
public virtual IDynamicTypedSelectQueryBuilder<T> FromTyped<T>(string alias = null, bool noLock = false)
{
// TODO: Make it more readable and maitainable
DynamicTypedSelectQueryBuilder<T> builder = new DynamicTypedSelectQueryBuilder<T>(this);
if (noLock)
{
if (string.IsNullOrEmpty(alias))
builder.From(x => x(typeof(T)).NoLock());
else
builder.From(x => x(typeof(T)).As(alias).NoLock());
}
else
{
if (string.IsNullOrEmpty(alias))
builder.From(x => x(typeof(T)));
else
builder.From(x => x(typeof(T)).As(alias));
}
return builder;
}
/// <summary>Adds to the <code>FROM</code> clause using a typed scope builder with evolving join arity.</summary>
/// <typeparam name="T">Type which can be represented in database.</typeparam>
/// <param name="alias">Table alias.</param>
/// <param name="noLock">use no lock.</param>
/// <returns>Scope builder instance.</returns>
public virtual IDynamicTypedSelectScopeQueryBuilder<T> FromTypedScope<T>(string alias = null, bool noLock = false)
{
DynamicTypedSelectQueryBuilder<T> builder = (DynamicTypedSelectQueryBuilder<T>)FromTyped<T>(alias, noLock);
string resolvedAlias = string.IsNullOrEmpty(alias) ? builder.Tables[0].Alias ?? builder.Tables[0].Name : alias;
return new DynamicTypedSelectScopeQueryBuilder<T>(builder, resolvedAlias);
}
/// <summary>Adds to the <code>FROM</code> clause using <see cref="Type"/>.</summary>
/// <param name="t">Type which can be represented in database.</param>
/// <returns>This instance to permit chaining.</returns>
@@ -504,6 +544,14 @@ namespace DynamORM
return new DynamicInsertQueryBuilder(this).Table(typeof(T));
}
/// <summary>Adds to the <code>INSERT INTO</code> clause using <see cref="Type"/>.</summary>
/// <typeparam name="T">Type which can be represented in database.</typeparam>
/// <returns>This instance to permit chaining.</returns>
public virtual IDynamicTypedInsertQueryBuilder<T> InsertTyped<T>()
{
return new DynamicTypedInsertQueryBuilder<T>(this, true);
}
/// <summary>Adds to the <code>INSERT INTO</code> clause using <see cref="Type"/>.</summary>
/// <param name="t">Type which can be represented in database.</param>
/// <returns>This instance to permit chaining.</returns>
@@ -611,6 +659,14 @@ namespace DynamORM
return new DynamicUpdateQueryBuilder(this).Table(typeof(T));
}
/// <summary>Adds to the <code>UPDATE</code> clause using <see cref="Type"/>.</summary>
/// <typeparam name="T">Type which can be represented in database.</typeparam>
/// <returns>This instance to permit chaining.</returns>
public virtual IDynamicTypedUpdateQueryBuilder<T> UpdateTyped<T>()
{
return new DynamicTypedUpdateQueryBuilder<T>(this, true);
}
/// <summary>Adds to the <code>UPDATE</code> clause using <see cref="Type"/>.</summary>
/// <param name="t">Type which can be represented in database.</param>
/// <returns>This instance to permit chaining.</returns>
@@ -832,6 +888,14 @@ namespace DynamORM
return new DynamicDeleteQueryBuilder(this).Table(typeof(T));
}
/// <summary>Adds to the <code>DELETE FROM</code> clause using <see cref="Type"/>.</summary>
/// <typeparam name="T">Type which can be represented in database.</typeparam>
/// <returns>This instance to permit chaining.</returns>
public virtual IDynamicTypedDeleteQueryBuilder<T> DeleteTyped<T>()
{
return new DynamicTypedDeleteQueryBuilder<T>(this, true);
}
/// <summary>Adds to the <code>DELETE FROM</code> clause using <see cref="Type"/>.</summary>
/// <param name="t">Type which can be represented in database.</param>
/// <returns>This instance to permit chaining.</returns>

View File

@@ -0,0 +1,26 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
namespace DynamORM.TypedSql
{
/// <summary>Render context used by typed SQL DSL nodes.</summary>
public interface ITypedSqlRenderContext
{
/// <summary>Resolve mapped column for given model member.</summary>
string ResolveColumn(Type modelType, string memberName, string alias);
/// <summary>Render value as SQL parameter or literal fragment.</summary>
string RenderValue(object value);
/// <summary>Decorate SQL identifier.</summary>
string DecorateName(string name);
/// <summary>Render subquery SQL and merge any parameters into current context.</summary>
string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query);
}
}

226
DynamORM/TypedSql/Sql.cs Normal file
View File

@@ -0,0 +1,226 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Collections.Generic;
using DynamORM.Builders;
namespace DynamORM.TypedSql
{
/// <summary>Entry point for the typed SQL DSL.</summary>
public static class Sql
{
/// <summary>Create parameterized value expression.</summary>
public static TypedSqlExpression<T> Val<T>(T value)
{
return new TypedSqlValueExpression<T>(value);
}
/// <summary>Create parameterized value expression.</summary>
public static TypedSqlExpression<object> Val(object value)
{
return new TypedSqlValueExpression<object>(value);
}
/// <summary>Create typed table context for an explicitly named alias, including joined aliases.</summary>
public static TypedTableContext<T> Table<T>(string alias)
{
return new TypedTableContext<T>(alias);
}
/// <summary>Create raw SQL expression.</summary>
public static TypedSqlExpression<T> Raw<T>(string sql)
{
return new TypedSqlRawExpression<T>(sql);
}
/// <summary>Create raw ORDER BY fragment.</summary>
public static TypedSqlOrderExpression RawOrder(string sql)
{
return new TypedSqlRawOrderExpression(Raw<object>(sql));
}
/// <summary>Create generic function call.</summary>
public static TypedSqlExpression<T> Func<T>(string name, params TypedSqlExpression[] arguments)
{
return new TypedSqlFunctionExpression<T>(name, arguments);
}
/// <summary>Create COUNT(*) expression.</summary>
public static TypedSqlExpression<int> Count()
{
return Raw<int>("COUNT(*)");
}
/// <summary>Create COUNT(expr) expression.</summary>
public static TypedSqlExpression<int> Count(TypedSqlExpression expression)
{
return Func<int>("COUNT", expression);
}
/// <summary>Create COALESCE expression.</summary>
public static TypedSqlExpression<T> Coalesce<T>(params TypedSqlExpression[] expressions)
{
return Func<T>("COALESCE", expressions);
}
/// <summary>Create SUM expression.</summary>
public static TypedSqlExpression<T> Sum<T>(TypedSqlExpression expression)
{
return Func<T>("SUM", expression);
}
/// <summary>Create AVG expression.</summary>
public static TypedSqlExpression<T> Avg<T>(TypedSqlExpression expression)
{
return Func<T>("AVG", expression);
}
/// <summary>Create MIN expression.</summary>
public static TypedSqlExpression<T> Min<T>(TypedSqlExpression expression)
{
return Func<T>("MIN", expression);
}
/// <summary>Create MAX expression.</summary>
public static TypedSqlExpression<T> Max<T>(TypedSqlExpression expression)
{
return Func<T>("MAX", expression);
}
/// <summary>Create ABS expression.</summary>
public static TypedSqlExpression<T> Abs<T>(TypedSqlExpression expression)
{
return Func<T>("ABS", expression);
}
/// <summary>Create UPPER expression.</summary>
public static TypedSqlExpression<string> Upper(TypedSqlExpression expression)
{
return Func<string>("UPPER", expression);
}
/// <summary>Create LOWER expression.</summary>
public static TypedSqlExpression<string> Lower(TypedSqlExpression expression)
{
return Func<string>("LOWER", expression);
}
/// <summary>Create TRIM expression.</summary>
public static TypedSqlExpression<string> Trim(TypedSqlExpression expression)
{
return Func<string>("TRIM", expression);
}
/// <summary>Create LENGTH expression.</summary>
public static TypedSqlExpression<int> Length(TypedSqlExpression expression)
{
return Func<int>("LENGTH", expression);
}
/// <summary>Create NULLIF expression.</summary>
public static TypedSqlExpression<T> NullIf<T>(TypedSqlExpression left, TypedSqlExpression right)
{
return Func<T>("NULLIF", left, right);
}
/// <summary>Create CURRENT_TIMESTAMP expression.</summary>
public static TypedSqlExpression<DateTime> CurrentTimestamp()
{
return Raw<DateTime>("CURRENT_TIMESTAMP");
}
/// <summary>Create scalar subquery expression.</summary>
public static TypedSqlExpression<T> SubQuery<T>(IDynamicSelectQueryBuilder query)
{
return new TypedSqlSubQueryExpression<T>(query);
}
/// <summary>Create scalar typed subquery expression without manually constructing the builder.</summary>
public static TypedSqlExpression<TResult> SubQuery<TModel, TResult>(DynamicDatabase db, Func<IDynamicTypedSelectQueryBuilder<TModel>, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false)
{
if (db == null)
throw new ArgumentNullException("db");
if (configure == null)
throw new ArgumentNullException("configure");
return new TypedSqlSubQueryExpression<TResult>(configure(db.FromTyped<TModel>(alias, noLock)));
}
/// <summary>Create scalar typed subquery expression using the scoped typed builder API.</summary>
public static TypedSqlExpression<TResult> SubQueryScope<TModel, TResult>(DynamicDatabase db, Func<IDynamicTypedSelectScopeQueryBuilder<TModel>, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false)
{
if (db == null)
throw new ArgumentNullException("db");
if (configure == null)
throw new ArgumentNullException("configure");
return new TypedSqlSubQueryExpression<TResult>(configure(db.FromTypedScope<TModel>(alias, noLock)));
}
/// <summary>Create EXISTS predicate.</summary>
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
{
return new TypedSqlExistsPredicate(query);
}
/// <summary>Create EXISTS predicate from typed subquery factory.</summary>
public static TypedSqlPredicate Exists<TModel>(DynamicDatabase db, Func<IDynamicTypedSelectQueryBuilder<TModel>, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false)
{
if (db == null)
throw new ArgumentNullException("db");
if (configure == null)
throw new ArgumentNullException("configure");
return new TypedSqlExistsPredicate(configure(db.FromTyped<TModel>(alias, noLock)));
}
/// <summary>Create EXISTS predicate from scoped typed subquery factory.</summary>
public static TypedSqlPredicate ExistsScope<TModel>(DynamicDatabase db, Func<IDynamicTypedSelectScopeQueryBuilder<TModel>, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false)
{
if (db == null)
throw new ArgumentNullException("db");
if (configure == null)
throw new ArgumentNullException("configure");
return new TypedSqlExistsPredicate(configure(db.FromTypedScope<TModel>(alias, noLock)));
}
/// <summary>Create CASE expression builder.</summary>
public static TypedSqlCaseBuilder<T> Case<T>()
{
return new TypedSqlCaseBuilder<T>();
}
}
/// <summary>Builder for CASE expressions.</summary>
/// <typeparam name="T">Result type.</typeparam>
public sealed class TypedSqlCaseBuilder<T>
{
private readonly IList<KeyValuePair<TypedSqlPredicate, TypedSqlExpression>> _cases = new List<KeyValuePair<TypedSqlPredicate, TypedSqlExpression>>();
/// <summary>Add WHEN ... THEN ... clause.</summary>
public TypedSqlCaseBuilder<T> When(TypedSqlPredicate predicate, object value)
{
_cases.Add(new KeyValuePair<TypedSqlPredicate, TypedSqlExpression>(
predicate,
value as TypedSqlExpression ?? Sql.Val(value)));
return this;
}
/// <summary>Finalize CASE expression with ELSE clause.</summary>
public TypedSqlExpression<T> Else(object value)
{
return new TypedSqlCaseExpression<T>(_cases, value as TypedSqlExpression ?? Sql.Val(value));
}
/// <summary>Finalize CASE expression without ELSE clause.</summary>
public TypedSqlExpression<T> End()
{
return new TypedSqlCaseExpression<T>(_cases, null);
}
}
}

View File

@@ -0,0 +1,572 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using DynamORM.Builders;
namespace DynamORM.TypedSql
{
/// <summary>Base selectable SQL fragment for the typed DSL.</summary>
public abstract class TypedSqlSelectable
{
internal abstract string Render(ITypedSqlRenderContext context);
}
/// <summary>Base SQL expression for the typed DSL.</summary>
public abstract class TypedSqlExpression : TypedSqlSelectable
{
/// <summary>Add arithmetic expression.</summary>
public static TypedSqlExpression<object> operator +(TypedSqlExpression left, TypedSqlExpression right)
{
return new TypedSqlBinaryExpression(left, "+", right);
}
/// <summary>Subtract arithmetic expression.</summary>
public static TypedSqlExpression<object> operator -(TypedSqlExpression left, TypedSqlExpression right)
{
return new TypedSqlBinaryExpression(left, "-", right);
}
/// <summary>Multiply arithmetic expression.</summary>
public static TypedSqlExpression<object> operator *(TypedSqlExpression left, TypedSqlExpression right)
{
return new TypedSqlBinaryExpression(left, "*", right);
}
/// <summary>Divide arithmetic expression.</summary>
public static TypedSqlExpression<object> operator /(TypedSqlExpression left, TypedSqlExpression right)
{
return new TypedSqlBinaryExpression(left, "/", right);
}
/// <summary>Modulo arithmetic expression.</summary>
public static TypedSqlExpression<object> operator %(TypedSqlExpression left, TypedSqlExpression right)
{
return new TypedSqlBinaryExpression(left, "%", right);
}
/// <summary>Alias this expression in SELECT clause.</summary>
public TypedSqlAliasedExpression As(string alias)
{
return new TypedSqlAliasedExpression(this, alias);
}
/// <summary>Order ascending.</summary>
public TypedSqlOrderExpression Asc()
{
return new TypedSqlOrderExpression(this, true);
}
/// <summary>Order descending.</summary>
public TypedSqlOrderExpression Desc()
{
return new TypedSqlOrderExpression(this, false);
}
/// <summary>Add expression to another value.</summary>
public TypedSqlExpression<object> Add(object value)
{
return new TypedSqlBinaryExpression(this, "+", value as TypedSqlExpression ?? Sql.Val(value));
}
/// <summary>Subtract another value.</summary>
public TypedSqlExpression<object> Sub(object value)
{
return new TypedSqlBinaryExpression(this, "-", value as TypedSqlExpression ?? Sql.Val(value));
}
/// <summary>Multiply by another value.</summary>
public TypedSqlExpression<object> Mul(object value)
{
return new TypedSqlBinaryExpression(this, "*", value as TypedSqlExpression ?? Sql.Val(value));
}
/// <summary>Divide by another value.</summary>
public TypedSqlExpression<object> Div(object value)
{
return new TypedSqlBinaryExpression(this, "/", value as TypedSqlExpression ?? Sql.Val(value));
}
/// <summary>Modulo by another value.</summary>
public TypedSqlExpression<object> Mod(object value)
{
return new TypedSqlBinaryExpression(this, "%", value as TypedSqlExpression ?? Sql.Val(value));
}
/// <summary>Equality predicate.</summary>
public TypedSqlPredicate Eq(object value)
{
return new TypedSqlBinaryPredicate(this, "=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value));
}
/// <summary>Inequality predicate.</summary>
public TypedSqlPredicate NotEq(object value)
{
return new TypedSqlBinaryPredicate(this, "<>", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value));
}
/// <summary>Greater-than predicate.</summary>
public TypedSqlPredicate Gt(object value)
{
return new TypedSqlBinaryPredicate(this, ">", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value));
}
/// <summary>Greater-than-or-equal predicate.</summary>
public TypedSqlPredicate Gte(object value)
{
return new TypedSqlBinaryPredicate(this, ">=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value));
}
/// <summary>Less-than predicate.</summary>
public TypedSqlPredicate Lt(object value)
{
return new TypedSqlBinaryPredicate(this, "<", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value));
}
/// <summary>Less-than-or-equal predicate.</summary>
public TypedSqlPredicate Lte(object value)
{
return new TypedSqlBinaryPredicate(this, "<=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value));
}
/// <summary>IS NULL predicate.</summary>
public TypedSqlPredicate IsNull()
{
return new TypedSqlUnaryPredicate(this, "IS NULL");
}
/// <summary>IS NOT NULL predicate.</summary>
public TypedSqlPredicate IsNotNull()
{
return new TypedSqlUnaryPredicate(this, "IS NOT NULL");
}
/// <summary>LIKE predicate.</summary>
public TypedSqlPredicate Like(string pattern)
{
return new TypedSqlBinaryPredicate(this, "LIKE", Sql.Val(pattern));
}
/// <summary>IN predicate.</summary>
public TypedSqlPredicate In(params object[] values)
{
return new TypedSqlInPredicate(this, values);
}
/// <summary>IN predicate.</summary>
public TypedSqlPredicate In(IEnumerable values)
{
return new TypedSqlInPredicate(this, values);
}
/// <summary>NOT IN predicate.</summary>
public TypedSqlPredicate NotIn(params object[] values)
{
return new TypedSqlInPredicate(this, values, true);
}
/// <summary>NOT IN predicate.</summary>
public TypedSqlPredicate NotIn(IEnumerable values)
{
return new TypedSqlInPredicate(this, values, true);
}
/// <summary>BETWEEN predicate.</summary>
public TypedSqlPredicate Between(object lower, object upper)
{
return new TypedSqlBetweenPredicate(this, lower is TypedSqlExpression ? (TypedSqlExpression)lower : Sql.Val(lower), upper is TypedSqlExpression ? (TypedSqlExpression)upper : Sql.Val(upper));
}
/// <summary>Starts-with LIKE predicate.</summary>
public TypedSqlPredicate StartsWith(string value)
{
return Like((value ?? string.Empty) + "%");
}
/// <summary>Ends-with LIKE predicate.</summary>
public TypedSqlPredicate EndsWith(string value)
{
return Like("%" + (value ?? string.Empty));
}
/// <summary>Contains LIKE predicate.</summary>
public TypedSqlPredicate Contains(string value)
{
return Like("%" + (value ?? string.Empty) + "%");
}
}
/// <summary>Typed SQL expression.</summary>
public abstract class TypedSqlExpression<T> : TypedSqlExpression
{
}
/// <summary>Typed SQL predicate expression.</summary>
public abstract class TypedSqlPredicate : TypedSqlExpression<bool>
{
/// <summary>Combine with AND.</summary>
public TypedSqlPredicate And(TypedSqlPredicate right)
{
return new TypedSqlCombinedPredicate(this, "AND", right);
}
/// <summary>Combine with OR.</summary>
public TypedSqlPredicate Or(TypedSqlPredicate right)
{
return new TypedSqlCombinedPredicate(this, "OR", right);
}
/// <summary>Negate predicate.</summary>
public TypedSqlPredicate Not()
{
return new TypedSqlNegatedPredicate(this);
}
}
/// <summary>Aliased SQL expression.</summary>
public sealed class TypedSqlAliasedExpression : TypedSqlSelectable
{
private readonly TypedSqlExpression _expression;
private readonly string _alias;
internal TypedSqlAliasedExpression(TypedSqlExpression expression, string alias)
{
_expression = expression;
_alias = alias;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("{0} AS {1}", _expression.Render(context), context.DecorateName(_alias));
}
}
/// <summary>Ordered SQL expression.</summary>
public class TypedSqlOrderExpression : TypedSqlSelectable
{
private readonly TypedSqlExpression _expression;
private readonly bool _ascending;
private readonly string _nullOrdering;
private readonly bool _raw;
internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending, string nullOrdering = null, bool raw = false)
{
_expression = expression;
_ascending = ascending;
_nullOrdering = nullOrdering;
_raw = raw;
}
/// <summary>Append NULLS FIRST ordering.</summary>
public TypedSqlOrderExpression NullsFirst()
{
return new TypedSqlOrderExpression(_expression, _ascending, "NULLS FIRST", _raw);
}
/// <summary>Append NULLS LAST ordering.</summary>
public TypedSqlOrderExpression NullsLast()
{
return new TypedSqlOrderExpression(_expression, _ascending, "NULLS LAST", _raw);
}
internal override string Render(ITypedSqlRenderContext context)
{
string rendered = _raw
? _expression.Render(context)
: string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC");
if (!string.IsNullOrEmpty(_nullOrdering))
rendered = string.Format("{0} {1}", rendered, _nullOrdering);
return rendered;
}
}
internal sealed class TypedSqlRawOrderExpression : TypedSqlOrderExpression
{
internal TypedSqlRawOrderExpression(TypedSqlExpression expression)
: base(expression, true, null, true)
{
}
}
internal sealed class TypedSqlBinaryExpression : TypedSqlExpression<object>
{
private readonly TypedSqlExpression _left;
private readonly string _operator;
private readonly TypedSqlExpression _right;
internal TypedSqlBinaryExpression(TypedSqlExpression left, string op, TypedSqlExpression right)
{
_left = left;
_operator = op;
_right = right;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context));
}
}
internal sealed class TypedSqlColumnExpression<T> : TypedSqlExpression<T>
{
private readonly Type _modelType;
private readonly string _memberName;
private readonly string _alias;
internal TypedSqlColumnExpression(Type modelType, string memberName, string alias)
{
_modelType = modelType;
_memberName = memberName;
_alias = alias;
}
internal override string Render(ITypedSqlRenderContext context)
{
return context.ResolveColumn(_modelType, _memberName, _alias);
}
}
internal sealed class TypedSqlValueExpression<T> : TypedSqlExpression<T>, ITypedSqlNullValue
{
private readonly object _value;
internal TypedSqlValueExpression(object value)
{
_value = value;
}
internal override string Render(ITypedSqlRenderContext context)
{
return context.RenderValue(_value);
}
public bool IsNullValue
{
get { return _value == null; }
}
}
internal sealed class TypedSqlRawExpression<T> : TypedSqlExpression<T>
{
private readonly string _sql;
internal TypedSqlRawExpression(string sql)
{
_sql = sql;
}
internal override string Render(ITypedSqlRenderContext context)
{
return _sql;
}
}
internal sealed class TypedSqlFunctionExpression<T> : TypedSqlExpression<T>
{
private readonly string _name;
private readonly IList<TypedSqlExpression> _arguments;
internal TypedSqlFunctionExpression(string name, params TypedSqlExpression[] arguments)
{
_name = name;
_arguments = arguments ?? new TypedSqlExpression[0];
}
internal override string Render(ITypedSqlRenderContext context)
{
List<string> rendered = new List<string>();
foreach (TypedSqlExpression argument in _arguments)
rendered.Add(argument.Render(context));
return string.Format("{0}({1})", _name, string.Join(", ", rendered.ToArray()));
}
}
internal sealed class TypedSqlUnaryPredicate : TypedSqlPredicate
{
private readonly TypedSqlExpression _expression;
private readonly string _operator;
internal TypedSqlUnaryPredicate(TypedSqlExpression expression, string op)
{
_expression = expression;
_operator = op;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("({0} {1})", _expression.Render(context), _operator);
}
}
internal sealed class TypedSqlBinaryPredicate : TypedSqlPredicate
{
private readonly TypedSqlExpression _left;
private readonly string _operator;
private readonly TypedSqlExpression _right;
internal TypedSqlBinaryPredicate(TypedSqlExpression left, string op, TypedSqlExpression right)
{
_left = left;
_operator = op;
_right = right;
}
internal override string Render(ITypedSqlRenderContext context)
{
string op = _operator;
TypedSqlValueExpression<object> objRight = _right as TypedSqlValueExpression<object>;
if ((objRight != null && objRight.IsNullValue) || (_right is ITypedSqlNullValue && ((ITypedSqlNullValue)_right).IsNullValue))
op = _operator == "=" ? "IS" : _operator == "<>" ? "IS NOT" : _operator;
return string.Format("({0} {1} {2})", _left.Render(context), op, _right.Render(context));
}
}
internal interface ITypedSqlNullValue
{
bool IsNullValue { get; }
}
internal sealed class TypedSqlInPredicate : TypedSqlPredicate
{
private readonly TypedSqlExpression _left;
private readonly IEnumerable _values;
private readonly bool _negated;
internal TypedSqlInPredicate(TypedSqlExpression left, IEnumerable values, bool negated = false)
{
_left = left;
_values = values;
_negated = negated;
}
internal override string Render(ITypedSqlRenderContext context)
{
List<string> rendered = new List<string>();
foreach (object value in _values)
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
if (rendered.Count == 0)
return _negated ? "(1 = 1)" : "(1 = 0)";
return string.Format("({0} {1}({2}))", _left.Render(context), _negated ? "NOT IN" : "IN", string.Join(", ", rendered.ToArray()));
}
}
internal sealed class TypedSqlBetweenPredicate : TypedSqlPredicate
{
private readonly TypedSqlExpression _left;
private readonly TypedSqlExpression _lower;
private readonly TypedSqlExpression _upper;
internal TypedSqlBetweenPredicate(TypedSqlExpression left, TypedSqlExpression lower, TypedSqlExpression upper)
{
_left = left;
_lower = lower;
_upper = upper;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("({0} BETWEEN {1} AND {2})", _left.Render(context), _lower.Render(context), _upper.Render(context));
}
}
internal sealed class TypedSqlCombinedPredicate : TypedSqlPredicate
{
private readonly TypedSqlPredicate _left;
private readonly string _operator;
private readonly TypedSqlPredicate _right;
internal TypedSqlCombinedPredicate(TypedSqlPredicate left, string op, TypedSqlPredicate right)
{
_left = left;
_operator = op;
_right = right;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context));
}
}
internal sealed class TypedSqlNegatedPredicate : TypedSqlPredicate
{
private readonly TypedSqlPredicate _predicate;
internal TypedSqlNegatedPredicate(TypedSqlPredicate predicate)
{
_predicate = predicate;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("(NOT {0})", _predicate.Render(context));
}
}
internal sealed class TypedSqlCaseExpression<T> : TypedSqlExpression<T>
{
private readonly IList<KeyValuePair<TypedSqlPredicate, TypedSqlExpression>> _cases;
private readonly TypedSqlExpression _elseExpression;
internal TypedSqlCaseExpression(IList<KeyValuePair<TypedSqlPredicate, TypedSqlExpression>> cases, TypedSqlExpression elseExpression)
{
_cases = cases;
_elseExpression = elseExpression;
}
internal override string Render(ITypedSqlRenderContext context)
{
List<string> items = new List<string>();
items.Add("CASE");
foreach (KeyValuePair<TypedSqlPredicate, TypedSqlExpression> item in _cases)
items.Add(string.Format("WHEN {0} THEN {1}", item.Key.Render(context), item.Value.Render(context)));
if (_elseExpression != null)
items.Add(string.Format("ELSE {0}", _elseExpression.Render(context)));
items.Add("END");
return string.Join(" ", items.ToArray());
}
}
internal sealed class TypedSqlSubQueryExpression<T> : TypedSqlExpression<T>
{
private readonly IDynamicSelectQueryBuilder _query;
internal TypedSqlSubQueryExpression(IDynamicSelectQueryBuilder query)
{
_query = query;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("({0})", context.RenderSubQuery(_query));
}
}
internal sealed class TypedSqlExistsPredicate : TypedSqlPredicate
{
private readonly IDynamicSelectQueryBuilder _query;
internal TypedSqlExistsPredicate(IDynamicSelectQueryBuilder query)
{
_query = query;
}
internal override string Render(ITypedSqlRenderContext context)
{
return string.Format("(EXISTS ({0}))", context.RenderSubQuery(_query));
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*/
using System;
using System.Linq.Expressions;
namespace DynamORM.TypedSql
{
/// <summary>Typed table context used by the typed SQL DSL.</summary>
/// <typeparam name="T">Mapped entity type.</typeparam>
public sealed class TypedTableContext<T>
{
internal TypedTableContext(string alias)
{
Alias = alias;
}
/// <summary>Gets table alias used by the current query.</summary>
public string Alias { get; private set; }
/// <summary>Creates a mapped column expression.</summary>
public TypedSqlExpression<TValue> Col<TValue>(Expression<Func<T, TValue>> selector)
{
if (selector == null)
throw new ArgumentNullException("selector");
MemberExpression member = selector.Body as MemberExpression;
if (member == null && selector.Body is UnaryExpression)
member = ((UnaryExpression)selector.Body).Operand as MemberExpression;
if (member == null)
throw new NotSupportedException(string.Format("Column selector must target a mapped property: {0}", selector));
return new TypedSqlColumnExpression<TValue>(typeof(T), member.Member.Name, Alias);
}
/// <summary>Select all columns from this typed table context.</summary>
public TypedSqlExpression<object> All()
{
return Sql.Raw<object>(string.IsNullOrEmpty(Alias) ? "*" : Alias + ".*");
}
}
}

View File

@@ -9,6 +9,7 @@ This documentation is based on:
- [Quick Start](quick-start.md)
- [Dynamic Table API](dynamic-table-api.md)
- [Fluent Builder API](fluent-builder-api.md)
- [Typed Fluent Syntax](typed-fluent-syntax.md)
- [Mapping and Entities](mapping-and-entities.md)
- [Transactions and Disposal](transactions-and-disposal.md)
- [Stored Procedures](stored-procedures.md)
@@ -36,3 +37,5 @@ DynamORM provides two major usage styles:
- `db.Insert<T>() / db.Update<T>() / db.Delete<T>()`
The dynamic API is concise and fast to use. The fluent builder API gives stronger composition and explicit SQL control.
For the newer property-mapped typed fluent syntax, see [Typed Fluent Syntax](typed-fluent-syntax.md).

View File

@@ -1,5 +1,7 @@
# Fluent Builder API
For the newer property-mapped typed fluent API, see [Typed Fluent Syntax](typed-fluent-syntax.md). This page focuses on the original dynamic fluent builder semantics and parser behavior.
The fluent API is built around:
- `IDynamicSelectQueryBuilder`

View File

@@ -52,6 +52,22 @@ using (var query = db.From("users").Where("id", 19).SelectColumn("first"))
}
```
## First Query (Typed Fluent Syntax)
```csharp
using (var db = new DynamicDatabase(SQLiteFactory.Instance, "Data Source=app.db;", options))
{
var cmd = db.FromTyped<User>("u")
.SelectSql(u => u.Col(x => x.Code))
.WhereSql(u => u.Col(x => x.Id).Eq(19));
var value = cmd.ScalarAs<string>();
Console.WriteLine(value);
}
```
For multi-join typed queries, prefer `FromTypedScope(...)`. Full details are documented in [Typed Fluent Syntax](typed-fluent-syntax.md).
## Insert, Update, Delete
```csharp

367
docs/typed-fluent-syntax.md Normal file
View File

@@ -0,0 +1,367 @@
# Typed Fluent Syntax
This page documents the new typed fluent syntax added alongside the original dynamic fluent builder API.
It is intended for cases where:
- you want property-based IntelliSense instead of raw column names
- you want mapped property validation in fluent SQL construction
- you still want explicit SQL composition instead of LINQ translation
The old dynamic fluent API remains unchanged. This typed API is additive.
## Two Typed Styles
There are currently two typed select styles:
- positional typed builder:
- `db.FromTyped<T>("alias")`
- scoped typed builder:
- `db.FromTypedScope<T>("alias")`
Recommendation:
- use `FromTypedScope(...)` for multi-join queries
- use `FromTyped(...)` for simple root-table typed queries or when you want direct positional generic overloads
## Root Typed Builder
Basic example:
```csharp
var cmd = db.FromTyped<User>("u")
.SelectSql(u => u.Col(x => x.Code))
.WhereSql(u => u.Col(x => x.Id).Eq(1));
```
Mapped properties are resolved through entity metadata:
```csharp
[Table(Name = "sample_users")]
public class User
{
[Column("id_user", true)]
public long Id { get; set; }
[Column("user_code")]
public string Code { get; set; }
}
```
So:
- `u.Col(x => x.Id)` renders mapped column `u."id_user"`
- `u.Col(x => x.Code)` renders mapped column `u."user_code"`
## Core Building Blocks
The typed DSL is built around:
- `TypedTableContext<T>`
- `TypedSqlExpression<T>`
- `TypedSqlPredicate`
- `TypedSqlOrderExpression`
- `Sql.*` helpers
Common building blocks:
```csharp
u.Col(x => x.Id)
Sql.Val(1)
Sql.Count()
Sql.Coalesce<string>(u.Col(x => x.Code), Sql.Val("N/A"))
Sql.Case<string>().When(...).Else(...)
```
## Positional Typed Builder
`FromTyped(...)` supports typed joins and then exposes joined tables by position.
Example:
```csharp
var cmd = db.FromTyped<User>("u")
.Join<UserClient>(j => j.Left().As("c").OnSql((u, c) =>
u.Col(x => x.Id).Eq(c.Col(x => x.UserId))))
.SelectSql<UserClient>(
(u, c) => u.Col(x => x.Id).As("user_id"),
(u, c) => c.Col(x => x.ClientCode).As("client_code"))
.WhereSql<UserClient>((u, c) =>
c.Col(x => x.IsDeleted).Eq(0));
```
This style is explicit, but becomes noisy as join count grows because join types must be repeated in generic parameters.
## Scoped Typed Builder
`FromTypedScope(...)` solves the repeated-generic problem by changing builder type after each join.
Example:
```csharp
var cmd = db.FromTypedScope<User>("u")
.Join<UserClient>(j => j.Left().As("c").OnSql((u, c) =>
u.Col(x => x.Id).Eq(c.Col(x => x.UserId))))
.Join<UserRole>(j => j.Left().As("r").OnSql((u, c, r) =>
c.Col(x => x.UserId).Eq(r.Col(x => x.UserId))))
.SelectSql(
(u, c, r) => u.Col(x => x.Id),
(u, c, r) => r.Col(x => x.RoleName).As("role_name"))
.WhereSql((u, c, r) =>
r.Col(x => x.RoleId).Gt(0));
```
Important behavior:
- each `Join<TJoin>(...)` extends the available typed lambda parameters
- later joins can reference previously joined tables inside `OnSql(...)`
- this is the preferred typed syntax for larger join graphs
Current implementation supports up to `16` typed tables in one scoped chain.
## Join Syntax
Typed joins support:
- `Inner()`
- `Join()`
- `Left()`
- `Right()`
- `Full()`
- `LeftOuter()`
- `RightOuter()`
- `FullOuter()`
- `Type("...")` for custom join keywords
- `NoLock()`
- `OnSql(...)`
- `OnRaw(...)`
Examples:
```csharp
.Join<UserClient>(j => j.Left().As("c").OnSql((u, c) =>
u.Col(x => x.Id).Eq(c.Col(x => x.UserId))))
.Join<UserClient>(j => j.Type("CROSS APPLY").As("c").NoLock())
.Join<UserRole>(j => j.Left().As("r").OnRaw("c.\"User_Id\" = r.\"User_Id\""))
```
`OnSql(...)` is usually the best option because it stays mapped and typed.
## Selecting Columns
Single-column select:
```csharp
.SelectSql(u => u.Col(x => x.Code))
```
Aliased select:
```csharp
.SelectSql(u => u.Col(x => x.Code).As("user_code"))
```
Wildcard:
```csharp
.SelectSql(u => u.All())
```
Multi-table scoped select:
```csharp
.SelectSql(
(u, c, r) => u.Col(x => x.Id).As("user_id"),
(u, c, r) => c.Col(x => x.ClientCode).As("client_code"),
(u, c, r) => r.Col(x => x.RoleName).As("role_name"))
```
## Predicates
Available predicate patterns include:
- `Eq`
- `NotEq`
- `Gt`
- `Gte`
- `Lt`
- `Lte`
- `IsNull`
- `IsNotNull`
- `Like`
- `StartsWith`
- `EndsWith`
- `Contains`
- `In`
- `NotIn`
- `Between`
- `And`
- `Or`
- `Not`
Example:
```csharp
.WhereSql((u, c) =>
u.Col(x => x.Code).StartsWith("ADM")
.And(c.Col(x => x.IsDeleted).Eq(0))
.And(c.Col(x => x.ClientCode).IsNotNull()))
```
## Functions and Expressions
Standard helpers:
- `Sql.Count()`
- `Sql.Count(expr)`
- `Sql.Sum(...)`
- `Sql.Avg(...)`
- `Sql.Min(...)`
- `Sql.Max(...)`
- `Sql.Abs(...)`
- `Sql.Upper(...)`
- `Sql.Lower(...)`
- `Sql.Trim(...)`
- `Sql.Length(...)`
- `Sql.NullIf(...)`
- `Sql.Coalesce(...)`
- `Sql.CurrentTimestamp()`
Examples:
```csharp
.SelectSql(
u => Sql.Count().As("cnt"),
u => Sql.Coalesce<string>(u.Col(x => x.Code), Sql.Val("N/A")).As("code_value"))
.HavingSql((u, c) => Sql.Count(c.Col(x => x.UserId)).Gt(0))
```
Arithmetic is also supported:
```csharp
.SelectSql(u => (u.Col(x => x.Id) + Sql.Val(1)).As("plus_one"))
```
## CASE Expressions
```csharp
.SelectSql(u => Sql.Case<string>()
.When(u.Col(x => x.Code).Eq("A"), "Alpha")
.When(u.Col(x => x.Code).Eq("B"), "Beta")
.Else("Other")
.As("category"))
```
Use `CASE` when expression logic becomes too awkward for simple `COALESCE` or comparison predicates.
## Grouping and Ordering
Grouping:
```csharp
.GroupBySql((u, c) => u.Col(x => x.Id))
```
Ordering:
```csharp
.OrderBySql(
(u, c) => c.Col(x => x.ClientCode).Asc(),
(u, c) => Sql.Count(c.Col(x => x.UserId)).Desc())
```
Null ordering helpers are also available:
- `NullsFirst()`
- `NullsLast()`
Raw order fragments:
```csharp
.OrderBySql(u => Sql.RawOrder("2 DESC"))
```
## Custom SQL Escape Hatches
For cases not covered by built-in helpers:
- `Sql.Raw<T>(...)`
- `Sql.Func<T>(...)`
- `OnRaw(...)`
Examples:
```csharp
Sql.Raw<int>("ROW_NUMBER() OVER(ORDER BY u.\"id_user\")")
Sql.Func<string>("CUSTOM_FUNC", u.Col(x => x.Code), Sql.Val(5))
```
Use these when you need provider-specific or advanced SQL fragments, but keep normal query construction on typed helpers where possible.
## Scoped Subqueries
Two subquery helper families exist:
- classic typed helper:
- `Sql.SubQuery<TModel, TResult>(...)`
- `Sql.Exists<TModel>(...)`
- scoped typed helper:
- `Sql.SubQueryScope<TModel, TResult>(...)`
- `Sql.ExistsScope<TModel>(...)`
Use the scoped helpers when the subquery itself benefits from `FromTypedScope(...)`.
Example:
```csharp
.SelectSql(u => Sql.SubQueryScope<User, string>(
db,
sq => sq
.Join<UserClient>(j => j.Left().As("c").OnSql((x, c) =>
x.Col(a => a.Id).Eq(c.Col(a => a.UserId))))
.Join<UserRole>(j => j.Left().As("r").OnSql((x, c, r) =>
c.Col(a => a.UserId).Eq(r.Col(a => a.UserId))))
.SelectSql((x, c, r) => r.Col(a => a.RoleName)),
"x").As("role_name"))
```
`ExistsScope(...)` works the same way, but returns a predicate.
## Modify Builders
Typed SQL DSL also exists for modify builders:
- `InsertTyped<T>()`
- `UpdateTyped<T>()`
- `DeleteTyped<T>()`
Examples:
```csharp
db.UpdateTyped<User>()
.SetSql(u => u.Code, u => Sql.Coalesce<string>(Sql.Val("900"), u.Col(x => x.Code)))
.WhereSql(u => u.Col(x => x.Id).Eq(1));
db.InsertTyped<User>()
.InsertSql(u => u.Code, u => Sql.Val("901"));
db.DeleteTyped<User>()
.WhereSql(u => u.Col(x => x.Id).Eq(2));
```
## Practical Guidance
- prefer `FromTypedScope(...)` once a query has more than one or two joins
- use `OnSql(...)` instead of `OnRaw(...)` whenever mappings exist
- use `Sql.Func(...)` and `Sql.Raw(...)` sparingly as escape hatches
- use `SubQueryScope(...)` / `ExistsScope(...)` when the subquery itself is multi-join and typed
- keep the original dynamic fluent builder for cases where fully dynamic alias/member construction is still the better fit
## Related Pages
- [Fluent Builder API](fluent-builder-api.md)
- [Mapping and Entities](mapping-and-entities.md)
- [Test-Driven Examples](test-driven-examples.md)