Files
Umbraco-CMS/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Persistence/NPocoTests/NPocoSqlTemplateTests.cs
2022-01-18 15:23:53 +00:00

218 lines
10 KiB
C#

// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using Microsoft.Extensions.Options;
using Moq;
using NPoco;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Mappers;
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
using Umbraco.Cms.Tests.Common.TestHelpers;
using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Persistence.NPocoTests
{
[TestFixture]
public class NPocoSqlTemplateTests
{
[Test]
public void SqlTemplates()
{
var sqlContext = new SqlContext(new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())), DatabaseType.SqlServer2012, 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
Sql<ISqlContext> sql = sqlTemplates.Get("xxx", s => s
.SelectAll()
.From("zbThing1")
.Where("id=@id", new { id = SqlTemplate.Arg("id") })).Sql(new { id = 1 });
Sql<ISqlContext> sql2 = sqlTemplates.Get("xxx", x => throw new InvalidOperationException("Should be cached.")).Sql(1);
Sql<ISqlContext> 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 NullableDateMapper() };
var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init(), mappers);
var sqlContext = new SqlContext(new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())), DatabaseType.SQLCe, factory);
var sqlTemplates = new SqlTemplates(sqlContext);
const string sqlBase = "SELECT [zbThing1].[id] AS [Id], [zbThing1].[name] AS [Name] FROM [zbThing1] WHERE ";
SqlTemplate template = sqlTemplates.Get("sql1", s => s.Select<Thing1Dto>().From<Thing1Dto>()
.Where<Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("value")));
Sql<ISqlContext> 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<Thing1Dto>().From<Thing1Dto>()
.Where<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<Thing1Dto>().From<Thing1Dto>()
.Where<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<Thing1Dto>().From<Thing1Dto>()
.Where<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<Thing1Dto>().From<Thing1Dto>()
.Where<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<Thing1Dto>().From<Thing1Dto>()
.Where<Thing1Dto>(x => x.Id == SqlTemplate.Arg<int>("i"))
.Where<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<Thing1Dto>().From<Thing1Dto>()
.WhereIn<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<Thing1Dto>().From<Thing1Dto>()
.WhereIn<Thing1Dto>(x => x.Id, SqlTemplate.ArgIn<int>("i"))
.Where<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<Thing1Dto>().From<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]);
}
[TableName("zbThing1")]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
public class Thing1Dto
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
}
}