Migrate NPocoTests

This commit is contained in:
Mole
2020-10-22 14:12:07 +02:00
parent 4f80ebd788
commit ba262648d9
7 changed files with 85 additions and 54 deletions

View File

@@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using NUnit.Framework;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Tests.Integration.Implementations;
using Umbraco.Tests.Integration.Testing;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing;
namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.NPocoTests
{
// FIXME: npoco - is this still appropriate?
//
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class NPocoBulkInsertTests : UmbracoIntegrationTest
{
private TestHelper _testHelper = new TestHelper();
private IProfilingLogger ProfilingLogger => _testHelper.ProfilingLogger;
[NUnit.Framework.Ignore("Ignored because you need to configure your own SQL Server to test this with")]
[Test]
public void Can_Bulk_Insert_Native_Sql_Server_Bulk_Inserts()
{
// create the db
// prob not what we want, this is not a real database, but hey, the test is ignored anyways
// we'll fix this when we have proper testing infrastructure
// var dbSqlServer = TestObjects.GetUmbracoSqlServerDatabase(new NullLogger<UmbracoDatabase>());
var provider = ScopeProvider;
using (var scope = ScopeProvider.CreateScope())
{
// Still no what we want, but look above.
var dbSqlServer = scope.Database;
//drop the table
dbSqlServer.Execute("DROP TABLE [umbracoServer]");
//re-create it
dbSqlServer.Execute(@"CREATE TABLE [umbracoServer](
[id] [int] IDENTITY(1,1) NOT NULL,
[address] [nvarchar](500) NOT NULL,
[computerName] [nvarchar](255) NOT NULL,
[registeredDate] [datetime] NOT NULL CONSTRAINT [DF_umbracoServer_registeredDate] DEFAULT (getdate()),
[lastNotifiedDate] [datetime] NOT NULL,
[isActive] [bit] NOT NULL,
[isMaster] [bit] NOT NULL,
CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)");
var data = new List<ServerRegistrationDto>();
for (var i = 0; i < 1000; i++)
{
data.Add(new ServerRegistrationDto
{
ServerAddress = "address" + i,
ServerIdentity = "computer" + i,
DateRegistered = DateTime.Now,
IsActive = true,
DateAccessed = DateTime.Now
});
}
using (var tr = dbSqlServer.GetTransaction())
{
dbSqlServer.BulkInsertRecords(data);
tr.Complete();
}
// Assert
Assert.That(dbSqlServer.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(1000));
}
}
[Test]
public void Can_Bulk_Insert_Native_Sql_Bulk_Inserts()
{
var servers = new List<ServerRegistrationDto>();
for (var i = 0; i < 1000; i++)
{
servers.Add(new ServerRegistrationDto
{
ServerAddress = "address" + i,
ServerIdentity = "computer" + i,
DateRegistered = DateTime.Now,
IsActive = true,
DateAccessed = DateTime.Now
});
}
// Act
using (ProfilingLogger.TraceDuration<NPocoBulkInsertTests>("starting insert", "finished insert"))
{
using (var scope = ScopeProvider.CreateScope())
{
scope.Database.BulkInsertRecords(servers);
scope.Complete();
}
}
// Assert
using (var scope = ScopeProvider.CreateScope())
{
Assert.That(scope.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(1000));
}
}
[Test]
public void Can_Bulk_Insert_Native_Sql_Bulk_Inserts_Transaction_Rollback()
{
var servers = new List<ServerRegistrationDto>();
for (var i = 0; i < 1000; i++)
{
servers.Add(new ServerRegistrationDto
{
ServerAddress = "address" + i,
ServerIdentity = "computer" + i,
DateRegistered = DateTime.Now,
IsActive = true,
DateAccessed = DateTime.Now
});
}
// Act
using (ProfilingLogger.TraceDuration<NPocoBulkInsertTests>("starting insert", "finished insert"))
{
using (var scope = ScopeProvider.CreateScope())
{
scope.Database.BulkInsertRecords(servers);
//don't call complete here - the trans will be rolled back
}
}
// Assert
using (var scope = ScopeProvider.CreateScope())
{
Assert.That(scope.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(0));
}
}
[Test]
public void Generate_Bulk_Import_Sql()
{
var servers = new List<ServerRegistrationDto>();
for (var i = 0; i < 2; i++)
{
servers.Add(new ServerRegistrationDto
{
ServerAddress = "address" + i,
ServerIdentity = "computer" + i,
DateRegistered = DateTime.Now,
IsActive = true,
DateAccessed = DateTime.Now
});
}
IDbCommand[] commands;
using (var scope = ScopeProvider.CreateScope())
{
commands = scope.Database.GenerateBulkInsertCommands(servers.ToArray());
scope.Complete();
}
// Assert
Assert.That(commands[0].CommandText,
Is.EqualTo("INSERT INTO [umbracoServer] ([umbracoServer].[address], [umbracoServer].[computerName], [umbracoServer].[registeredDate], [umbracoServer].[lastNotifiedDate], [umbracoServer].[isActive], [umbracoServer].[isMaster]) VALUES (@0,@1,@2,@3,@4,@5), (@6,@7,@8,@9,@10,@11)"));
}
[Test]
public void Generate_Bulk_Import_Sql_Exceeding_Max_Params()
{
var servers = new List<ServerRegistrationDto>();
for (var i = 0; i < 1500; i++)
{
servers.Add(new ServerRegistrationDto
{
ServerAddress = "address" + i,
ServerIdentity = "computer" + i,
DateRegistered = DateTime.Now,
IsActive = true,
DateAccessed = DateTime.Now,
IsMaster = true
});
}
IDbCommand[] commands;
using (var scope = ScopeProvider.CreateScope())
{
commands = scope.Database.GenerateBulkInsertCommands(servers.ToArray());
scope.Complete();
}
// Assert
Assert.That(commands.Length, Is.EqualTo(5));
foreach (var s in commands.Select(x => x.CommandText))
{
Assert.LessOrEqual(Regex.Matches(s, "@\\d+").Count, 2000);
}
}
}
}

View File

@@ -0,0 +1,647 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NPoco;
using NUnit.Framework;
using Umbraco.Core.Persistence;
using Umbraco.Tests.Integration.Testing;
using Umbraco.Tests.Testing;
namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.NPocoTests
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)]
public class NPocoFetchTests : UmbracoIntegrationTest
{
[SetUp]
protected void SeedDatabase()
{
using (var scope = ScopeProvider.CreateScope())
{
InsertData(scope.Database);
scope.Complete();
}
}
private static void InsertData(IDatabase database)
{
database.Execute(@"
CREATE TABLE zbThing1 (
id int PRIMARY KEY NOT NULL,
name NVARCHAR(255) NULL
);");
database.Insert(new Thing1Dto
{
Id = 1,
Name = "one"
});
database.Insert(new Thing1Dto
{
Id = 2,
Name = "two"
});
database.Execute(@"
CREATE TABLE zbThing2 (
id int PRIMARY KEY NOT NULL,
name NVARCHAR(255) NULL,
thingId int NULL
);");
database.Insert(new Thing2Dto
{
Id = 1,
Name = "uno",
ThingId = 1
});
database.Insert(new Thing2Dto
{
Id = 2,
Name = "due",
ThingId = 2
});
database.Insert(new Thing2Dto
{
Id = 3,
Name = "tri",
ThingId = 1
});
database.Execute(@"
CREATE TABLE zbThingGroup (
id int PRIMARY KEY NOT NULL,
name NVARCHAR(255) NULL
);");
database.Insert(new ThingGroupDto
{
Id = 1,
Name = "g-one"
});
database.Insert(new ThingGroupDto
{
Id = 2,
Name = "g-two"
});
database.Insert(new ThingGroupDto
{
Id = 3,
Name = "g-three"
});
database.Execute(@"
CREATE TABLE zbThing2Group (
thingId int NOT NULL,
groupId int NOT NULL
);");
database.Insert(new Thing2GroupDto
{
ThingId = 1,
GroupId = 1
});
database.Insert(new Thing2GroupDto
{
ThingId = 1,
GroupId = 2
});
database.Insert(new Thing2GroupDto
{
ThingId = 2,
GroupId = 2
});
database.Insert(new Thing2GroupDto
{
ThingId = 3,
GroupId = 3
});
database.Execute(@"
CREATE TABLE zbThingA1 (
id int PRIMARY KEY NOT NULL,
name NVARCHAR(255) NULL
);");
database.Execute(@"
CREATE TABLE zbThingA2 (
id int PRIMARY KEY NOT NULL,
name NVARCHAR(255) NULL
);");
database.Execute(@"
CREATE TABLE zbThingA3 (
id int PRIMARY KEY NOT NULL,
name NVARCHAR(255) NULL
);");
database.Execute(@"
CREATE TABLE zbThingA12 (
thing1id int NOT NULL,
thing2id int NOT NULL,
name NVARCHAR(255) NOT NULL
);");
}
[Test]
public void TestSimple()
{
// fetching a simple POCO
using (var scope = ScopeProvider.CreateScope())
{
// this is the raw SQL, but it's better to use expressions and no magic strings!
//var sql = @"
// SELECT zbThing1.id, zbThing1.name
// FROM zbThing1";
var sql = scope.SqlContext.Sql()
.Select<Thing1Dto>()
.From<Thing1Dto>();
var dtos = scope.Database.Fetch<Thing1Dto>(sql);
Assert.AreEqual(2, dtos.Count);
Assert.AreEqual("one", dtos.First(x => x.Id == 1).Name);
}
}
[Test]
public void TestOneToOne()
{
// fetching a POCO that contains the ID of another POCO,
// and fetching that other POCO at the same time
using (var scope = ScopeProvider.CreateScope())
{
// this is the raw SQL, but it's better to use expressions and no magic strings!
//var sql = @"
// SELECT zbThing2.id, zbThing2.name, zbThing2.thingId,
// zbThing1.id Thing__id, zbThing1.name Thing__name
// FROM zbThing2
// JOIN zbThing1 ON zbThing2.thingId=zbThing1.id";
var sql = scope.SqlContext.Sql()
.Select<Thing2Dto>(r => r.Select(x => x.Thing))
.From<Thing2Dto>()
.InnerJoin<Thing1Dto>().On<Thing2Dto, Thing1Dto>((t2, t1) => t2.ThingId == t1.Id);
var dtos = scope.Database.Fetch<Thing2Dto>(sql);
Assert.AreEqual(3, dtos.Count);
Assert.AreEqual("uno", dtos.First(x => x.Id == 1).Name);
Assert.IsNotNull(dtos.First(x => x.Id == 1).Thing);
Assert.AreEqual("one", dtos.First(x => x.Id == 1).Thing.Name);
}
}
[Test]
public void TestOneToManyOnOne()
{
// fetching a POCO that has a list of other POCOs,
// and fetching these POCOs at the same time,
// with a pk/fk relationship
// for one single POCO
using (var scope = ScopeProvider.CreateScope())
{
// this is the raw SQL, but it's better to use expressions and no magic strings!
//var dtos = scope.Database.FetchOneToMany<Thing3Dto>(x => x.Things, x => x.Id, @"
// SELECT zbThing1.id AS Id, zbThing1.name AS Name,
// zbThing2.id AS Things__Id, zbThing2.name AS Things__Name, zbThing2.thingId AS Things__ThingId
// FROM zbThing1
// JOIN zbThing2 ON zbThing1.id=zbThing2.thingId
// WHERE zbThing1.id=1");
var sql = scope.SqlContext.Sql()
.Select<Thing3Dto>(r => r.Select(x => x.Things))
.From<Thing3Dto>()
.InnerJoin<Thing2Dto>().On<Thing3Dto, Thing2Dto>(left => left.Id, right => right.ThingId)
.Where<Thing3Dto>(x => x.Id == 1);
//var dtos = scope.Database.FetchOneToMany<Thing3Dto>(x => x.Things, x => x.Id, sql);
var dtos = scope.Database.FetchOneToMany<Thing3Dto>(x => x.Things, sql);
Assert.AreEqual(1, dtos.Count);
var dto1 = dtos.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto1);
Assert.AreEqual("one", dto1.Name);
Assert.IsNotNull(dto1.Things);
Assert.AreEqual(2, dto1.Things.Count);
var dto2 = dto1.Things.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto2);
Assert.AreEqual("uno", dto2.Name);
}
}
[Test]
public void TestOneToManyOnMany()
{
// fetching a POCO that has a list of other POCOs,
// and fetching these POCOs at the same time,
// with a pk/fk relationship
// for several POCOs
//
// the ORDER BY clause (matching x => x.Id) is required
// for proper aggregation to take place
using (var scope = ScopeProvider.CreateScope())
{
// this is the raw SQL, but it's better to use expressions and no magic strings!
//var sql = @"
// SELECT zbThing1.id AS Id, zbThing1.name AS Name,
// zbThing2.id AS Things__Id, zbThing2.name AS Things__Name, zbThing2.thingId AS Things__ThingId
// FROM zbThing1
// JOIN zbThing2 ON zbThing1.id=zbThing2.thingId
// ORDER BY zbThing1.id";
var sql = scope.SqlContext.Sql()
.Select<Thing3Dto>(r => r.Select(x => x.Things)) // select Thing3Dto, and Thing2Dto for Things
.From<Thing3Dto>()
.InnerJoin<Thing2Dto>().On<Thing3Dto, Thing2Dto>(left => left.Id, right => right.ThingId)
.OrderBy<Thing3Dto>(x => x.Id);
var dtos = scope.Database.FetchOneToMany<Thing3Dto>(x => x.Things, /*x => x.Id,*/ sql);
Assert.AreEqual(2, dtos.Count);
var dto1 = dtos.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto1);
Assert.AreEqual("one", dto1.Name);
Assert.IsNotNull(dto1.Things);
Assert.AreEqual(2, dto1.Things.Count);
var dto2 = dto1.Things.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto2);
Assert.AreEqual("uno", dto2.Name);
}
}
[Test]
public void TestOneToManyOnManyTemplate()
{
using (var scope = ScopeProvider.CreateScope())
{
scope.SqlContext.Templates.Clear();
var sql = scope.SqlContext.Templates.Get("xxx", s => s
.Select<Thing3Dto>(r => r.Select(x => x.Things)) // select Thing3Dto, and Thing2Dto for Things
.From<Thing3Dto>()
.InnerJoin<Thing2Dto>().On<Thing3Dto, Thing2Dto>(left => left.Id, right => right.ThingId)
.OrderBy<Thing3Dto>(x => x.Id)).Sql();
// cached
sql = scope.SqlContext.Templates.Get("xxx", s => throw new InvalidOperationException()).Sql();
var dtos = scope.Database.FetchOneToMany<Thing3Dto>(x => x.Things, /*x => x.Id,*/ sql);
Assert.AreEqual(2, dtos.Count);
var dto1 = dtos.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto1);
Assert.AreEqual("one", dto1.Name);
Assert.IsNotNull(dto1.Things);
Assert.AreEqual(2, dto1.Things.Count);
var dto2 = dto1.Things.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto2);
Assert.AreEqual("uno", dto2.Name);
}
}
[Test]
public void TestManyToMany()
{
// fetching a POCO that has a list of other POCOs,
// and fetching these POCOs at the same time,
// with an n-to-n intermediate table
//
// the ORDER BY clause (matching x => x.Id) is required
// for proper aggregation to take place
using (var scope = ScopeProvider.CreateScope())
{
// this is the raw SQL, but it's better to use expressions and no magic strings!
//var sql = @"
// SELECT zbThing1.id, zbThing1.name, zbThingGroup.id, zbThingGroup.name
// FROM zbThing1
// JOIN zbThing2Group ON zbThing1.id=zbThing2Group.thingId
// JOIN zbThingGroup ON zbThing2Group.groupId=zbThingGroup.id
// ORDER BY zbThing1.id";
var sql = scope.SqlContext.Sql()
.Select<Thing4Dto>(r => r.Select(x => x.Groups))
.From<Thing4Dto>()
.InnerJoin<Thing2GroupDto>().On<Thing4Dto, Thing2GroupDto>((t, t2g) => t.Id == t2g.ThingId)
.InnerJoin<ThingGroupDto>().On<Thing2GroupDto, ThingGroupDto>((t2g, tg) => t2g.GroupId == tg.Id)
.OrderBy<Thing4Dto>(x => x.Id);
var dtos = scope.Database.FetchOneToMany<Thing4Dto>(x => x.Groups, /*x => x.Id,*/ sql);
Assert.AreEqual(2, dtos.Count);
var dto1 = dtos.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto1);
Assert.AreEqual("one", dto1.Name);
Assert.IsNotNull(dto1.Groups);
Assert.AreEqual(2, dto1.Groups.Count);
var dto2 = dto1.Groups.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto2);
Assert.AreEqual("g-one", dto2.Name);
}
}
[Test]
public void TestCalculated()
{
// fetching a POCO that has a countof other POCOs,
// with an n-to-n intermediate table
using (var scope = ScopeProvider.CreateScope())
{
// this is the raw SQL, but it's better to use expressions and no magic strings!
//var sql = @"
// SELECT zbThing1.id, zbThing1.name, COUNT(zbThing2Group.groupId) as groupCount
// FROM zbThing1
// JOIN zbThing2Group ON zbThing1.id=zbThing2Group.thingId
// GROUP BY zbThing1.id, zbThing1.name";
var sql = scope.SqlContext.Sql()
.Select<Thing1Dto>()
.Append(", COUNT(zbThing2Group.groupId) AS groupCount") // FIXME:
.From<Thing1Dto>()
.InnerJoin<Thing2GroupDto>().On<Thing1Dto, Thing2GroupDto>((t, t2g) => t.Id == t2g.ThingId)
.GroupBy<Thing1Dto>(x => x.Id, x => x.Name);
var dtos = scope.Database.Fetch<Thing5Dto>(sql);
Assert.AreEqual(2, dtos.Count);
var dto1 = dtos.FirstOrDefault(x => x.Id == 1);
Assert.IsNotNull(dto1);
Assert.AreEqual("one", dto1.Name);
Assert.AreEqual(2, dto1.GroupCount);
var dto2 = dtos.FirstOrDefault(x => x.Id == 2);
Assert.IsNotNull(dto2);
Assert.AreEqual("two", dto2.Name);
Assert.AreEqual(1, dto2.GroupCount);
}
}
// no test for ReferenceType.Foreign at the moment
// it's more or less OneToOne, but NPoco manages the keys when
// inserting or updating
[Test]
public void TestSql()
{
using (var scope = ScopeProvider.CreateScope())
{
var sql = scope.SqlContext.Sql()
.SelectAll()
.From<Thing1Dto>()
.Where<Thing1Dto>(x => x.Id == 1);
var dto = scope.Database.Fetch<Thing1Dto>(sql).FirstOrDefault();
Assert.IsNotNull(dto);
Assert.AreEqual("one", dto.Name);
//var sql2 = new Sql(sql.SQL, new { id = 1 });
//WriteSql(sql2);
//dto = Database.Fetch<Thing1Dto>(sql2).FirstOrDefault();
//Assert.IsNotNull(dto);
//Assert.AreEqual("one", dto.Name);
var sql3 = new Sql(sql.SQL, 1);
dto = scope.Database.Fetch<Thing1Dto>(sql3).FirstOrDefault();
Assert.IsNotNull(dto);
Assert.AreEqual("one", dto.Name);
}
}
[Test]
public void TestMultipleOneToOne()
{
using (var scope = ScopeProvider.CreateScope())
{
var tA1A = new ThingA1Dto { Id = 1, Name = "a1_a" };
scope.Database.Insert(tA1A);
var tA1B = new ThingA1Dto { Id = 2, Name = "a1_b" };
scope.Database.Insert(tA1B);
var tA1C = new ThingA1Dto { Id = 3, Name = "a1_c" };
scope.Database.Insert(tA1C);
var tA2A = new ThingA2Dto { Id = 1, Name = "a2_a" };
scope.Database.Insert(tA2A);
var tA2B = new ThingA2Dto { Id = 2, Name = "a2_b" };
scope.Database.Insert(tA2B);
var tA2C = new ThingA2Dto { Id = 3, Name = "a2_c" };
scope.Database.Insert(tA2C);
var tA3A = new ThingA3Dto { Id = 1, Name = "a3_a" };
scope.Database.Insert(tA3A);
var tA3B = new ThingA3Dto { Id = 2, Name = "a3_b" };
scope.Database.Insert(tA3B);
var k1 = new ThingA12Dto { Name = "a", Thing1Id = tA1A.Id, Thing2Id = tA2A.Id };
scope.Database.Insert(k1);
var k2 = new ThingA12Dto { Name = "B", Thing1Id = tA1A.Id, Thing2Id = tA2B.Id };
scope.Database.Insert(k2);
var sql = @"SELECT a1.id, a1.name,
a2.id AS T2A__Id, a2.name AS T2A__Name, a3.id AS T2A__T3__Id, a3.name AS T2A__T3__Name,
a2x.id AS T2B__Id, a2x.name AS T2B__Name, a3x.id AS T2B__T3__Id, a3x.name AS T2B__T3__Name
FROM zbThingA1 a1
JOIN zbThingA12 a12 ON a1.id=a12.thing1id AND a12.name='a'
JOIN zbThingA2 a2 ON a12.thing2id=a2.id
JOIN zbThingA3 a3 ON a2.id=a3.id
JOIN zbThingA12 a12x ON a1.id=a12x.thing1id AND a12x.name='b'
JOIN zbThingA2 a2x ON a12x.thing2id=a2x.id
JOIN zbThingA3 a3x ON a2x.id=a3x.id
";
var ts = scope.Database.Fetch<ThingA1Dto>(sql);
Assert.AreEqual(1, ts.Count);
var t = ts.First();
Assert.AreEqual("a1_a", t.Name);
Assert.AreEqual("a2_a", t.T2A.Name);
Assert.AreEqual("a2_b", t.T2B.Name);
Assert.AreEqual("a3_a", t.T2A.T3.Name);
Assert.AreEqual("a3_b", t.T2B.T3.Name);
scope.Complete();
}
}
[TableName("zbThing1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Thing1Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
[TableName("zbThing2")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Thing2Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("thingId")]
public int ThingId { get; set; }
// reference is required else value remains null
// columnName indicates which column has the id, referenceMembreName not needed if PK
[Reference(ReferenceType.OneToOne, ColumnName = "thingId"/*, ReferenceMemberName="id"*/)]
public Thing1Dto Thing { get; set; }
}
[TableName("zbThing1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Thing3Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
// reference is required else FetchOneToMany aggregation does not happen
// does not seem to require ReferenceMemberName="thingId", ColumnName not needed if PK
[Reference(ReferenceType.Many/*, ColumnName="id", ReferenceMemberName="thingId"*/)]
public List<Thing2Dto> Things { get; set; }
}
[TableName("zbThingGroup")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class ThingGroupDto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
[TableName("zbThing2Group")]
[PrimaryKey("thingId, groupId", AutoIncrement = false)]
[ExplicitColumns]
public class Thing2GroupDto
{
[Column("thingId")]
public int ThingId { get; set; }
[Column("groupId")]
public int GroupId { get; set; }
}
[TableName("zbThing1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Thing4Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
// reference is required else FetchOneToMany aggregation does not happen
// not sure ColumnName nor ReferenceMemberName make much sense here
[Reference(ReferenceType.Many/*, ColumnName="id", ReferenceMemberName="thingId"*/)]
public List<ThingGroupDto> Groups { get; set; }
}
[TableName("zbThing1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Thing5Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("groupCount")]
[ResultColumn] // not included in insert/update, not sql-generated
//[ComputedColumn] // not included in insert/update, sql-generated
public int GroupCount { get; set; }
}
[TableName("zbThingA1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class ThingA1Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne)]
public ThingA2Dto T2A { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne)]
public ThingA2Dto T2B { get; set; }
}
[TableName("zbThingA2")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class ThingA2Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne)]
public ThingA3Dto T3 { get; set; }
}
[TableName("zbThingA3")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class ThingA3Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
[TableName("zbThingA12")]
[ExplicitColumns]
public class ThingA12Dto
{
[Column("thing1id")]
public int Thing1Id { get; set; }
[Column("thing2id")]
public int Thing2Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
}
}

View File

@@ -0,0 +1,251 @@
using System.Collections.Generic;
using NPoco;
using NUnit.Framework;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Tests.Integration.Testing;
using Umbraco.Tests.Testing;
using static Umbraco.Core.Persistence.SqlExtensionsStatics;
namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.NPocoTests
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class NPocoSqlExtensionsTests : UmbracoIntegrationTest
{
private ISqlContext SqlContext => GetRequiredService<ISqlContext>();
private Sql<ISqlContext> Sql()
{
return NPoco.Sql.BuilderFor(SqlContext);
}
[Test]
public void WhereTest()
{
var sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == null);
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] is null))", sql.SQL, sql.SQL);
sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == 123);
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] = @0))", sql.SQL, sql.SQL);
var id = 123;
sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == id);
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] = @0))", sql.SQL, sql.SQL);
int? nid = 123;
sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == nid);
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] = @0))", sql.SQL, sql.SQL);
// but the above comparison fails if @0 is null
// what we want is something similar to:
sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => (nid == null && x.LanguageId == null) || (nid != null && x.LanguageId == nid));
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE ((((@0 is null) AND ([umbracoPropertyData].[languageId] is null)) OR ((@1 is not null) AND ([umbracoPropertyData].[languageId] = @2))))", sql.SQL, sql.SQL);
// new SqlNullableEquals method does it automatically
// 'course it would be nicer if '==' could do it
// see note in ExpressionVisitorBase for SqlNullableEquals
//sql = new Sql<ISqlContext>(SqlContext)
// .Select("*")
// .From<PropertyDataDto>()
// .Where<PropertyDataDto>(x => x.LanguageId.SqlNullableEquals(nid));
//Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE ((((@0 is null) AND ([umbracoPropertyData].[languageId] is null)) OR ((@0 is not null) AND ([umbracoPropertyData].[languageId] = @0))))", sql.SQL, sql.SQL);
// but, the expression above fails with SQL CE, 'specified argument for the function is not valid' in 'isnull' function
// so... compare with fallback values
sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId.SqlNullableEquals(nid, -1));
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE ((COALESCE([umbracoPropertyData].[languageId],@0) = COALESCE(@1,@0)))", sql.SQL, sql.SQL);
}
[Test]
public void SqlNullableEqualsTest()
{
int? a, b;
a = b = null;
Assert.IsTrue(a.SqlNullableEquals(b, -1));
b = 2;
Assert.IsFalse(a.SqlNullableEquals(b, -1));
a = 2;
Assert.IsTrue(a.SqlNullableEquals(b, -1));
b = null;
Assert.IsFalse(a.SqlNullableEquals(b, -1));
}
[Test]
public void WhereInValueFieldTest()
{
var sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<NodeDto>()
.WhereIn<NodeDto>(x => x.NodeId, new[] { 1, 2, 3 });
Assert.AreEqual("SELECT *\nFROM [umbracoNode]\nWHERE ([umbracoNode].[id] IN (@0,@1,@2))", sql.SQL);
}
[Test]
public void WhereInObjectFieldTest()
{
// this test used to fail because x => x.Text was evaluated as a lambda
// and returned "[umbracoNode].[text] = @0"... had to fix WhereIn.
var sql = new Sql<ISqlContext>(SqlContext)
.Select("*")
.From<NodeDto>()
.WhereIn<NodeDto>(x => x.Text, new[] { "a", "b", "c" });
Assert.AreEqual("SELECT *\nFROM [umbracoNode]\nWHERE ([umbracoNode].[text] IN (@0,@1,@2))", sql.SQL);
}
[Test]
public void SelectTests()
{
// select the whole DTO
var sql = Sql()
.Select<Dto1>()
.From<Dto1>();
Assert.AreEqual("SELECT [dto1].[id] AS [Id], [dto1].[name] AS [Name], [dto1].[value] AS [Value] FROM [dto1]", sql.SQL.NoCrLf());
// select only 1 field
sql = Sql()
.Select<Dto1>(x => x.Id)
.From<Dto1>();
Assert.AreEqual("SELECT [dto1].[id] AS [Id] FROM [dto1]", sql.SQL.NoCrLf());
// select 2 fields
sql = Sql()
.Select<Dto1>(x => x.Id, x => x.Name)
.From<Dto1>();
Assert.AreEqual("SELECT [dto1].[id] AS [Id], [dto1].[name] AS [Name] FROM [dto1]", sql.SQL.NoCrLf());
// select the whole DTO and a referenced DTO
sql = Sql()
.Select<Dto1>(r => r.Select(x => x.Dto2))
.From<Dto1>()
.InnerJoin<Dto2>().On<Dto1, Dto2>(left => left.Id, right => right.Dto1Id);
Assert.AreEqual(@"SELECT [dto1].[id] AS [Id], [dto1].[name] AS [Name], [dto1].[value] AS [Value]
, [dto2].[id] AS [Dto2__Id], [dto2].[dto1id] AS [Dto2__Dto1Id], [dto2].[name] AS [Dto2__Name]
FROM [dto1]
INNER JOIN [dto2] ON [dto1].[id] = [dto2].[dto1id]".NoCrLf(), sql.SQL.NoCrLf(), sql.SQL);
// select the whole DTO and nested referenced DTOs
sql = Sql()
.Select<Dto1>(r => r.Select(x => x.Dto2, r1 => r1.Select(x => x.Dto3)))
.From<Dto1>()
.InnerJoin<Dto2>().On<Dto1, Dto2>(left => left.Id, right => right.Dto1Id)
.InnerJoin<Dto3>().On<Dto2, Dto3>(left => left.Id, right => right.Dto2Id);
Assert.AreEqual(@"SELECT [dto1].[id] AS [Id], [dto1].[name] AS [Name], [dto1].[value] AS [Value]
, [dto2].[id] AS [Dto2__Id], [dto2].[dto1id] AS [Dto2__Dto1Id], [dto2].[name] AS [Dto2__Name]
, [dto3].[id] AS [Dto2__Dto3__Id], [dto3].[dto2id] AS [Dto2__Dto3__Dto2Id], [dto3].[name] AS [Dto2__Dto3__Name]
FROM [dto1]
INNER JOIN [dto2] ON [dto1].[id] = [dto2].[dto1id]
INNER JOIN [dto3] ON [dto2].[id] = [dto3].[dto2id]".NoCrLf(), sql.SQL.NoCrLf());
// select the whole DTO and referenced DTOs
sql = Sql()
.Select<Dto1>(r => r.Select(x => x.Dto2s))
.From<Dto1>()
.InnerJoin<Dto2>().On<Dto1, Dto2>(left => left.Id, right => right.Dto1Id);
Assert.AreEqual(@"SELECT [dto1].[id] AS [Id], [dto1].[name] AS [Name], [dto1].[value] AS [Value]
, [dto2].[id] AS [Dto2s__Id], [dto2].[dto1id] AS [Dto2s__Dto1Id], [dto2].[name] AS [Dto2s__Name]
FROM [dto1]
INNER JOIN [dto2] ON [dto1].[id] = [dto2].[dto1id]".NoCrLf(), sql.SQL.NoCrLf());
}
[Test]
public void SelectAliasTests()
{
// and select - not good
var sql = Sql()
.Select<Dto1>(x => x.Id)
.Select<Dto2>(x => x.Id);
Assert.AreEqual("SELECT [dto1].[id] AS [Id] SELECT [dto2].[id] AS [Id]".NoCrLf(), sql.SQL.NoCrLf());
// and select - good
sql = Sql()
.Select<Dto1>(x => x.Id)
.AndSelect<Dto2>(x => x.Id);
Assert.AreEqual("SELECT [dto1].[id] AS [Id] , [dto2].[id] AS [Id]".NoCrLf(), sql.SQL.NoCrLf());
// and select + alias
sql = Sql()
.Select<Dto1>(x => x.Id)
.AndSelect<Dto2>(x => Alias(x.Id, "id2"));
Assert.AreEqual("SELECT [dto1].[id] AS [Id] , [dto2].[id] AS [id2]".NoCrLf(), sql.SQL.NoCrLf());
}
[Test]
public void UpdateTests()
{
var sql = Sql()
.Update<DataTypeDto>(u => u.Set(x => x.EditorAlias, "Umbraco.ColorPicker"))
.Where<DataTypeDto>(x => x.EditorAlias == "Umbraco.ColorPickerAlias");
}
[TableName("dto1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Dto1
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("value")]
public int Value { get; set; }
[Reference]
public Dto2 Dto2 { get; set; }
[Reference]
public List<Dto2> Dto2s { get; set; }
}
[TableName("dto2")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Dto2
{
[Column("id")]
public int Id { get; set; }
[Column("dto1id")]
public int Dto1Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Reference]
public Dto3 Dto3 { get; set; }
}
[TableName("dto3")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Dto3
{
[Column("id")]
public int Id { get; set; }
[Column("dto2id")]
public int Dto2Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
}
}

View File

@@ -0,0 +1,204 @@
using System;
using Moq;
using NPoco;
using NUnit.Framework;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core;
using Umbraco.Persistance.SqlCe;
namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.NPocoTests
{
[TestFixture]
public class NPocoSqlTemplateTests
{
[Test]
public void SqlTemplates()
{
var sqlContext = new SqlContext(new SqlCeSyntaxProvider(), DatabaseType.SQLCe, Mock.Of<IPocoDataFactory>());
var sqlTemplates = new SqlTemplates(sqlContext);
// this can be used for queries that we know we'll use a *lot* and
// want to cache as a (static) template for ever, and ever - note
// that using a MemoryCache would allow us to set a size limit, or
// something equivalent, to reduce risk of memory explosion
var sql = sqlTemplates.Get("xxx", s => s
.SelectAll()
.From("zbThing1")
.Where("id=@id", new { id = SqlTemplate.Arg("id") })).Sql(new { id = 1 });
var sql2 = sqlTemplates.Get("xxx", x => throw new InvalidOperationException("Should be cached.")).Sql(1);
var sql3 = sqlTemplates.Get("xxx", x => throw new InvalidOperationException("Should be cached.")).Sql(new { id = 1 });
}
[Test]
public void SqlTemplateArgs()
{
var mappers = new NPoco.MapperCollection { new PocoMapper() };
var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init());
var sqlContext = new SqlContext(new SqlCeSyntaxProvider(), DatabaseType.SQLCe, factory);
var sqlTemplates = new SqlTemplates(sqlContext);
const string sqlBase = "SELECT [zbThing1].[id] AS [Id], [zbThing1].[name] AS [Name] FROM [zbThing1] WHERE ";
var template = sqlTemplates.Get("sql1", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("value")));
var sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
sql = template.Sql(123);
Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(123, sql.Arguments[0]);
template = sqlTemplates.Get("sql2", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("value")));
sql = template.Sql(new { value = "foo" });
Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
sql = template.Sql(new { value = 123 });
Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(123, sql.Arguments[0]);
Assert.Throws<InvalidOperationException>(() => template.Sql(new { xvalue = 123 }));
Assert.Throws<InvalidOperationException>(() => template.Sql(new { value = 123, xvalue = 456 }));
var i = 666;
template = sqlTemplates.Get("sql3", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == i));
sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
sql = template.Sql(123);
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(123, sql.Arguments[0]);
// but we cannot name them, because the arg name is the value of "i"
// so we have to explicitely create the argument
template = sqlTemplates.Get("sql4", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i")));
sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
sql = template.Sql(123);
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(123, sql.Arguments[0]);
// and thanks to a patched visitor, this now works
sql = template.Sql(new { i = "foo" });
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
sql = template.Sql(new { i = 123 });
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(123, sql.Arguments[0]);
Assert.Throws<InvalidOperationException>(() => template.Sql(new { j = 123 }));
Assert.Throws<InvalidOperationException>(() => template.Sql(new { i = 123, j = 456 }));
// now with more arguments
template = sqlTemplates.Get("sql4a", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i") && x.Name == SqlTemplate.Arg<string>("name")));
sql = template.Sql(0, 1);
Assert.AreEqual(sqlBase + "((([zbThing1].[id] = @0) AND ([zbThing1].[name] = @1)))", sql.SQL.NoCrLf());
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(0, sql.Arguments[0]);
Assert.AreEqual(1, sql.Arguments[1]);
template = sqlTemplates.Get("sql4b", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.Where<NPocoFetchTests.Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i"))
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("name")));
sql = template.Sql(0, 1);
Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0)) AND (([zbThing1].[name] = @1))", sql.SQL.NoCrLf());
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(0, sql.Arguments[0]);
Assert.AreEqual(1, sql.Arguments[1]);
// works, magic
template = sqlTemplates.Get("sql5", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.WhereIn<NPocoFetchTests.Thing1Dto>(x => x.Id, SqlTemplate.ArgIn<int>("i")));
sql = template.Sql("foo");
Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0))", sql.SQL.NoCrLf());
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
sql = template.Sql(new[] { 1, 2, 3 });
Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0,@1,@2))", sql.SQL.NoCrLf());
Assert.AreEqual(3, sql.Arguments.Length);
Assert.AreEqual(1, sql.Arguments[0]);
Assert.AreEqual(2, sql.Arguments[1]);
Assert.AreEqual(3, sql.Arguments[2]);
template = sqlTemplates.Get("sql5a", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
.WhereIn<NPocoFetchTests.Thing1Dto>(x => x.Id, SqlTemplate.ArgIn<int>("i"))
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("name")));
sql = template.Sql("foo", "bar");
Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0)) AND (([zbThing1].[name] = @1))", sql.SQL.NoCrLf());
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual("foo", sql.Arguments[0]);
Assert.AreEqual("bar", sql.Arguments[1]);
sql = template.Sql(new[] { 1, 2, 3 }, "bar");
Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0,@1,@2)) AND (([zbThing1].[name] = @3))", sql.SQL.NoCrLf());
Assert.AreEqual(4, sql.Arguments.Length);
Assert.AreEqual(1, sql.Arguments[0]);
Assert.AreEqual(2, sql.Arguments[1]);
Assert.AreEqual(3, sql.Arguments[2]);
Assert.AreEqual("bar", sql.Arguments[3]);
// note however that using WhereIn in a template means that the SQL is going
// to be parsed and arguments are going to be expanded etc - it *may* be a better
// idea to just add the WhereIn to a templated, immutable SQL template
// more fun...
template = sqlTemplates.Get("sql6", s => s.Select<NPocoFetchTests.Thing1Dto>().From<NPocoFetchTests.Thing1Dto>()
// do NOT do this, this is NOT a visited expression
//.Append(" AND whatever=@0", SqlTemplate.Arg<string>("j"))
// does not work anymore - due to proper TemplateArg
//// instead, directly name the argument
//.Append("AND whatever=@0", "j")
//.Append("AND whatever=@0", "k")
// instead, explicitely create the argument
.Append("AND whatever=@0", SqlTemplate.Arg("j"))
.Append("AND whatever=@0", SqlTemplate.Arg("k"))
);
sql = template.Sql(new { j = new[] { 1, 2, 3 }, k = "oops" });
Assert.AreEqual(sqlBase.TrimEnd("WHERE ") + "AND whatever=@0,@1,@2 AND whatever=@3", sql.SQL.NoCrLf());
Assert.AreEqual(4, sql.Arguments.Length);
Assert.AreEqual(1, sql.Arguments[0]);
Assert.AreEqual(2, sql.Arguments[1]);
Assert.AreEqual(3, sql.Arguments[2]);
Assert.AreEqual("oops", sql.Arguments[3]);
}
}
}

View File

@@ -0,0 +1,323 @@
using System;
using System.Diagnostics;
using NPoco;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Tests.Integration.Testing;
using Umbraco.Tests.Testing;
namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Persistence.NPocoTests
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class NPocoSqlTests : UmbracoIntegrationTest
{
private ISqlContext SqlContext => GetRequiredService<ISqlContext>();
private Sql<ISqlContext> Sql()
{
return NPoco.Sql.BuilderFor(SqlContext);
}
[Test]
public void Where_Clause_With_Starts_With_Additional_Parameters()
{
var content = new NodeDto() { NodeId = 123, Path = "-1,123" };
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Path.SqlStartsWith(content.Path, TextColumnType.NVarchar));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[path]) LIKE upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(content.Path + "%", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_Starts_With_By_Variable()
{
var content = new NodeDto() {NodeId = 123, Path = "-1,123"};
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Path.StartsWith(content.Path) && x.NodeId != content.NodeId);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ((upper([umbracoNode].[path]) LIKE upper(@0) AND ([umbracoNode].[id] <> @1)))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(content.Path + "%", sql.Arguments[0]);
Assert.AreEqual(content.NodeId, sql.Arguments[1]);
}
[Test]
public void Where_Clause_With_Not_Starts_With()
{
const int level = 1;
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Level == level && !x.Path.StartsWith("-20"));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ((([umbracoNode].[level] = @0) AND NOT (upper([umbracoNode].[path]) LIKE upper(@1))))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(level, sql.Arguments[0]);
Assert.AreEqual("-20%", sql.Arguments[1]);
}
[Test]
public void Where_Clause_With_EqualsFalse_Starts_With()
{
const int level = 1;
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Level == level && x.Path.StartsWith("-20") == false);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ((([umbracoNode].[level] = @0) AND NOT (upper([umbracoNode].[path]) LIKE upper(@1))))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(level, sql.Arguments[0]);
Assert.AreEqual("-20%", sql.Arguments[1]);
}
[Test]
public void Where_Clause_With_Equals_Clause()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Text.Equals("Hello@world.com"));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[text]) = upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("Hello@world.com", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_False_Boolean()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Trashed == false);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (NOT ([umbracoNode].[trashed] = @0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(true, sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_EqualsFalse_Boolean()
{
var sql = Sql().SelectAll().From<NodeDto>().Where<NodeDto>(x => x.Trashed == false);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (NOT ([umbracoNode].[trashed] = @0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(true, sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_Boolean()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Trashed);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[trashed] = @0)", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(true, sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_ToUpper()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Text.ToUpper() == "hello".ToUpper());
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ((upper([umbracoNode].[text]) = upper(@0)))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("hello", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_ToString()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Text == 1.ToString());
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (([umbracoNode].[text] = @0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("1", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_Wildcard()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Text.StartsWith("D"));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[text]) LIKE upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("D%", sql.Arguments[0]);
}
[Test]
public void Where_Clause_Single_Constant()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.NodeId == 2);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (([umbracoNode].[id] = @0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(2, sql.Arguments[0]);
}
[Test]
public void Where_Clause_And_Constant()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.NodeId != 2 && x.NodeId != 3);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ((([umbracoNode].[id] <> @0) AND ([umbracoNode].[id] <> @1)))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(2, sql.Arguments[0]);
Assert.AreEqual(3, sql.Arguments[1]);
}
[Test]
public void Where_Clause_Or_Constant()
{
var sql = Sql().SelectAll().From<NodeDto>()
.Where<NodeDto>(x => x.Text == "hello" || x.NodeId == 3);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ((([umbracoNode].[text] = @0) OR ([umbracoNode].[id] = @1)))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual("hello", sql.Arguments[0]);
Assert.AreEqual(3, sql.Arguments[1]);
}
[Test]
public void Where_Null()
{
var sql = Sql().SelectAll().From<NodeDto>().WhereNull<NodeDto>(x => x.NodeId);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (([umbracoNode].[id] IS NULL))", sql.SQL.Replace("\n", " "));
}
[Test]
public void Where_Not_Null()
{
var sql = Sql().SelectAll().From<NodeDto>().WhereNotNull<NodeDto>(x => x.NodeId);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (([umbracoNode].[id] IS NOT NULL))", sql.SQL.Replace("\n", " "));
}
[Test]
public void Where_Any()
{
var sql = Sql().SelectAll().From<NodeDto>().WhereAny(
s => s.Where<NodeDto>(x => x.NodeId == 1),
s => s.Where<NodeDto>(x => x.NodeId == 2));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (( (([umbracoNode].[id] = @0)) ) OR ( (([umbracoNode].[id] = @1)) ))", sql.SQL.Replace("\n", " "));
}
[Test]
public void Can_Select_From_With_Type()
{
var expected = Sql();
expected.SelectAll().From($"[{Constants.DatabaseSchema.Tables.Content}]");
var sql = Sql();
sql.SelectAll().From<ContentDto>();
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Debug.Print(sql.SQL);
}
[Test]
public void Can_InnerJoin_With_Types()
{
var expected = Sql();
expected.SelectAll()
.From($"[{Constants.DatabaseSchema.Tables.DocumentVersion}]")
.InnerJoin($"[{Constants.DatabaseSchema.Tables.ContentVersion}]")
.On($"[{Constants.DatabaseSchema.Tables.DocumentVersion}].[id] = [{Constants.DatabaseSchema.Tables.ContentVersion}].[id]");
var sql = Sql();
sql.SelectAll().From<DocumentVersionDto>()
.InnerJoin<ContentVersionDto>()
.On<DocumentVersionDto, ContentVersionDto>(left => left.Id, right => right.Id);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Debug.Print(sql.SQL);
}
[Test]
public void Can_OrderBy_With_Type()
{
var expected = Sql();
expected.SelectAll().From($"[{Constants.DatabaseSchema.Tables.Content}]").OrderBy($"([{Constants.DatabaseSchema.Tables.Content}].[contentTypeId])");
var sql = Sql();
sql.SelectAll().From<ContentDto>().OrderBy<ContentDto>(x => x.ContentTypeId);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Debug.Print(sql.SQL);
}
[Test]
public void Can_GroupBy_With_Type()
{
var expected = Sql();
expected.SelectAll().From($"[{Constants.DatabaseSchema.Tables.Content}]").GroupBy($"[{Constants.DatabaseSchema.Tables.Content}].[contentTypeId]");
var sql = Sql();
sql.SelectAll().From<ContentDto>().GroupBy<ContentDto>(x => x.ContentTypeId);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Debug.Print(sql.SQL);
}
[Test]
public void Can_Use_Where_Predicate()
{
var expected = Sql();
expected.SelectAll().From($"[{Constants.DatabaseSchema.Tables.Content}]").Where($"([{Constants.DatabaseSchema.Tables.Content}].[nodeId] = @0)", 1045);
var sql = Sql();
sql.SelectAll().From<ContentDto>().Where<ContentDto>(x => x.NodeId == 1045);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Debug.Print(sql.SQL);
}
[Test]
public void Can_Use_Where_And_Predicate()
{
var expected = Sql();
expected.SelectAll()
.From($"[{Constants.DatabaseSchema.Tables.Content}]")
.Where($"([{Constants.DatabaseSchema.Tables.Content}].[nodeId] = @0)", 1045)
.Where($"([{Constants.DatabaseSchema.Tables.Content}].[contentTypeId] = @0)", 1050);
var sql = Sql();
sql.SelectAll()
.From<ContentDto>()
.Where<ContentDto>(x => x.NodeId == 1045)
.Where<ContentDto>(x => x.ContentTypeId == 1050);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Debug.Print(sql.SQL);
}
[Test]
public void ForUpdate()
{
var sessionId = Guid.NewGuid();
var sql = Sql()
.SelectAll()
.From<UserLoginDto>()
.Where<UserLoginDto>(x => x.SessionId == sessionId);
Assert.AreEqual("SELECT * FROM [umbracoUserLogin] WHERE (([umbracoUserLogin].[sessionId] = @0))", sql.SQL.NoCrLf());
sql = sql.ForUpdate();
Assert.AreEqual("SELECT * FROM [umbracoUserLogin] WITH (UPDLOCK) WHERE (([umbracoUserLogin].[sessionId] = @0))", sql.SQL.NoCrLf());
}
}
}