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> <Copyright>Copyright © RUSSEK Software 2012-2026</Copyright>
<Company>RUSSEK Software</Company> <Company>RUSSEK Software</Company>
<Authors>Grzegorz Russek</Authors> <Authors>Grzegorz Russek</Authors>
<VersionPrefix>1.9</VersionPrefix> <VersionPrefix>2.0</VersionPrefix>
<Product>DynamORM</Product> <Product>DynamORM</Product>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -6,7 +6,7 @@
<Copyright>Copyright © RUSSEK Software 2012-2026</Copyright> <Copyright>Copyright © RUSSEK Software 2012-2026</Copyright>
<Company>RUSSEK Software</Company> <Company>RUSSEK Software</Company>
<Authors>Grzegorz Russek</Authors> <Authors>Grzegorz Russek</Authors>
<VersionPrefix>1.8</VersionPrefix> <VersionPrefix>2.0</VersionPrefix>
<RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl> <RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl>
<PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl> <PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl>
<Product>DynamORM</Product> <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. /// Tests the basic insert.
/// </summary> /// </summary>
[Test] [Test]
public void TestInsertBasic() public void TestTypedInsertBasic()
{ {
IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users"); 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> /// <summary>Implementation of dynamic insert query builder.</summary>
internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder
{ {
private string _columns; protected string _columns;
private string _values; protected string _values;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class. /// Initializes a new instance of the <see cref="DynamicInsertQueryBuilder"/> class.
@@ -221,4 +221,4 @@ namespace DynamORM.Builders.Implementation
#endregion IExtendedDisposable #endregion IExtendedDisposable
} }
} }

View File

@@ -42,17 +42,17 @@ using DynamORM.Mapper;
namespace DynamORM.Builders.Implementation namespace DynamORM.Builders.Implementation
{ {
/// <summary>Implementation of dynamic select query builder.</summary> /// <summary>Implementation of dynamic select query builder.</summary>
internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving
{ {
private int? _limit = null; private int? _limit = null;
private int? _offset = null; private int? _offset = null;
private bool _distinct = false; private bool _distinct = false;
private string _select; protected string _select;
private string _from; private string _from;
private string _join; protected string _join;
private string _groupby; protected string _groupby;
private string _orderby; protected string _orderby;
#region IQueryWithHaving #region IQueryWithHaving
@@ -1452,4 +1452,4 @@ namespace DynamORM.Builders.Implementation
#endregion IExtendedDisposable #endregion IExtendedDisposable
} }
} }

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> /// <summary>Update query builder.</summary>
internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
{ {
private string _columns; protected string _columns;
internal DynamicUpdateQueryBuilder(DynamicDatabase db) internal DynamicUpdateQueryBuilder(DynamicDatabase db)
: base(db) : base(db)
@@ -339,4 +339,4 @@ namespace DynamORM.Builders.Implementation
#endregion IExtendedDisposable #endregion IExtendedDisposable
} }
} }

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> <Copyright>Copyright © RUSSEK Software 2012-2026</Copyright>
<Company>RUSSEK Software</Company> <Company>RUSSEK Software</Company>
<Authors>Grzegorz Russek</Authors> <Authors>Grzegorz Russek</Authors>
<VersionPrefix>1.9</VersionPrefix> <VersionPrefix>2.0</VersionPrefix>
<RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl> <RepositoryUrl>https://git.dr4cul4.pl/RUSSEK-Software/DynamORM</RepositoryUrl>
<PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl> <PackageProjectUrl>https://dr4cul4.pl</PackageProjectUrl>
<Product>DynamORM</Product> <Product>DynamORM</Product>

View File

@@ -471,6 +471,46 @@ namespace DynamORM
return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias)); return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias));
} }
} }
/// <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> /// <summary>Adds to the <code>FROM</code> clause using <see cref="Type"/>.</summary>
/// <param name="t">Type which can be represented in database.</param> /// <param name="t">Type which can be represented in database.</param>
@@ -503,6 +543,14 @@ namespace DynamORM
{ {
return new DynamicInsertQueryBuilder(this).Table(typeof(T)); 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> /// <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> /// <param name="t">Type which can be represented in database.</param>
@@ -611,6 +659,14 @@ namespace DynamORM
return new DynamicUpdateQueryBuilder(this).Table(typeof(T)); 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> /// <summary>Adds to the <code>UPDATE</code> clause using <see cref="Type"/>.</summary>
/// <param name="t">Type which can be represented in database.</param> /// <param name="t">Type which can be represented in database.</param>
/// <returns>This instance to permit chaining.</returns> /// <returns>This instance to permit chaining.</returns>
@@ -832,6 +888,14 @@ namespace DynamORM
return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); 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> /// <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> /// <param name="t">Type which can be represented in database.</param>
/// <returns>This instance to permit chaining.</returns> /// <returns>This instance to permit chaining.</returns>
@@ -2006,16 +2070,16 @@ namespace DynamORM
#region IExtendedDisposable Members #region IExtendedDisposable Members
/// <summary>Performs application-defined tasks associated with freeing, /// <summary>Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.</summary> /// releasing, or resetting unmanaged resources.</summary>
public void Dispose() public void Dispose()
{ {
if (IsDisposed) if (IsDisposed)
return; return;
#if !DYNAMORM_OMMIT_OLDSYNTAX #if !DYNAMORM_OMMIT_OLDSYNTAX
List<DynamicTable> tables = TablesCache.Values.ToList(); List<DynamicTable> tables = TablesCache.Values.ToList();
TablesCache.Clear(); TablesCache.Clear();
tables.ForEach(t => t.Dispose()); tables.ForEach(t => t.Dispose());
tables.Clear(); tables.Clear();
@@ -2066,18 +2130,18 @@ namespace DynamORM
RemainingBuilders = null; RemainingBuilders = null;
} }
ClearSchema(); ClearSchema();
if (_proc != null) if (_proc != null)
_proc.Dispose(); _proc.Dispose();
_proc = null; _proc = null;
_tempConn = null; _tempConn = null;
IsDisposed = true; IsDisposed = true;
} }
/// <summary>Gets a value indicating whether this instance is disposed.</summary> /// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; } public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members #endregion IExtendedDisposable Members
} }
} }

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) - [Quick Start](quick-start.md)
- [Dynamic Table API](dynamic-table-api.md) - [Dynamic Table API](dynamic-table-api.md)
- [Fluent Builder API](fluent-builder-api.md) - [Fluent Builder API](fluent-builder-api.md)
- [Typed Fluent Syntax](typed-fluent-syntax.md)
- [Mapping and Entities](mapping-and-entities.md) - [Mapping and Entities](mapping-and-entities.md)
- [Transactions and Disposal](transactions-and-disposal.md) - [Transactions and Disposal](transactions-and-disposal.md)
- [Stored Procedures](stored-procedures.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>()` - `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. 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 # 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: The fluent API is built around:
- `IDynamicSelectQueryBuilder` - `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 ## Insert, Update, Delete
```csharp ```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)