More SqlTemplates
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
string AuthCookieName { get; }
|
||||
|
||||
string AuthCookieDomain { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A boolean indicating that by default the email address will be the username
|
||||
/// </summary>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
/// The alias of the external content indexer
|
||||
/// </summary>
|
||||
public const string ExternalIndexer = "ExternalIndexer";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The alias of the internal member searcher
|
||||
/// </summary>
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// date time is before the time, calculate onto next day)
|
||||
@@ -57,10 +57,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
@@ -71,10 +71,10 @@ namespace Umbraco.Core
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
ILogger Logger { get; }
|
||||
|
||||
ILocalMigration GetLocalMigration();
|
||||
|
||||
|
||||
ISqlContext SqlContext { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
/// </summary>
|
||||
public string VisitResult
|
||||
{
|
||||
get { return _visitResult; }
|
||||
get => _visitResult;
|
||||
set
|
||||
{
|
||||
if (Visited)
|
||||
|
||||
@@ -20,6 +20,41 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
_pd = sqlContext.PocoDataFactory.ForType(typeof(TDto));
|
||||
}
|
||||
|
||||
protected override string VisitMethodCall(MethodCallExpression m)
|
||||
{
|
||||
var declaring = m.Method.DeclaringType;
|
||||
if (declaring != typeof (SqlTemplate))
|
||||
return base.VisitMethodCall(m);
|
||||
|
||||
if (m.Method.Name != "Arg" && m.Method.Name != "ArgIn")
|
||||
throw new NotSupportedException($"Method SqlTemplate.{m.Method.Name} is not supported.");
|
||||
|
||||
var parameters = m.Method.GetParameters();
|
||||
if (parameters.Length != 1 || parameters[0].ParameterType != typeof (string))
|
||||
throw new NotSupportedException($"Method SqlTemplate.{m.Method.Name}({string.Join(", ", parameters.Select(x => x.ParameterType))} is not supported.");
|
||||
|
||||
var arg = m.Arguments[0];
|
||||
string name;
|
||||
if (arg.NodeType == ExpressionType.Constant)
|
||||
{
|
||||
name = arg.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// though... we probably should avoid doing this
|
||||
var member = Expression.Convert(arg, typeof (object));
|
||||
var lambda = Expression.Lambda<Func<object>>(member);
|
||||
var getter = lambda.Compile();
|
||||
name = getter().ToString();
|
||||
}
|
||||
|
||||
SqlParameters.Add(RemoveQuote(name));
|
||||
|
||||
return Visited
|
||||
? string.Empty
|
||||
: $"@{SqlParameters.Count - 1}";
|
||||
}
|
||||
|
||||
protected override string VisitMemberAccess(MemberExpression m)
|
||||
{
|
||||
if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(TDto))
|
||||
@@ -95,6 +130,10 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
_parameterName1 = lambda.Parameters[0].Name;
|
||||
_parameterName2 = lambda.Parameters[1].Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_parameterName1 = _parameterName2 = null;
|
||||
}
|
||||
return base.VisitLambda(lambda);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
@@ -29,22 +30,61 @@ namespace Umbraco.Core.Persistence
|
||||
// must pass the args in the proper order, faster
|
||||
public Sql<ISqlContext> Sql(params object[] args)
|
||||
{
|
||||
return new Sql<ISqlContext>(_sqlContext, _sql, args);
|
||||
// if the type is an "unspeakable name" it is an anonymous compiler-generated object
|
||||
// see https://stackoverflow.com/questions/9256594
|
||||
// => assume it's an anonymous type object containing named arguments
|
||||
// (of course this means we cannot use *real* objects here and need SqlNamed - bah)
|
||||
if (args.Length == 1 && args[0].GetType().Name.Contains("<"))
|
||||
return SqlNamed(args[0]);
|
||||
|
||||
if (args.Length != _args.Count)
|
||||
throw new ArgumentException("Invalid number of arguments.", nameof(args));
|
||||
|
||||
if (args.Length == 0)
|
||||
return new Sql<ISqlContext>(_sqlContext, true, _sql);
|
||||
|
||||
var isBuilt = !args.Any(x => x is IEnumerable);
|
||||
return new Sql<ISqlContext>(_sqlContext, isBuilt, _sql, args);
|
||||
}
|
||||
|
||||
// can pass named args, slower
|
||||
// so, not much different from what Where(...) does (ie reflection)
|
||||
public Sql<ISqlContext> SqlNamed(object nargs)
|
||||
{
|
||||
var isBuilt = true;
|
||||
var args = new object[_args.Count];
|
||||
var properties = nargs.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(nargs));
|
||||
for (var i = 0; i < _args.Count; i++)
|
||||
{
|
||||
if (!properties.TryGetValue(_args[i], out var value))
|
||||
throw new InvalidOperationException($"Invalid argument name \"{_args[i]}\".");
|
||||
throw new InvalidOperationException($"Missing argument \"{_args[i]}\".");
|
||||
args[i] = value;
|
||||
properties.Remove(_args[i]);
|
||||
|
||||
// if value is enumerable then we'll need to expand arguments
|
||||
if (value is IEnumerable)
|
||||
isBuilt = false;
|
||||
}
|
||||
return new Sql<ISqlContext>(_sqlContext, _sql, args);
|
||||
if (properties.Count > 0)
|
||||
throw new InvalidOperationException($"Unknown argument{(properties.Count > 1 ? "s" : "")}: {string.Join(", ", properties.Keys)}");
|
||||
return new Sql<ISqlContext>(_sqlContext, isBuilt, _sql, args);
|
||||
}
|
||||
|
||||
internal void WriteToConsole()
|
||||
{
|
||||
new Sql<ISqlContext>(_sqlContext, _sql, _args.Values.Cast<object>().ToArray()).WriteToConsole();
|
||||
}
|
||||
|
||||
public static T Arg<T>(string name)
|
||||
{
|
||||
return default (T);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> ArgIn<T>(string name)
|
||||
{
|
||||
// don't return an empty enumerable, as it breaks NPoco
|
||||
// fixme - should we cache these arrays?
|
||||
return new[] { default (T) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Tests.Benchmarks
|
||||
//typeof(DeepCloneBenchmarks),
|
||||
typeof(XmlPublishedContentInitBenchmarks),
|
||||
typeof(CtorInvokeBenchmarks),
|
||||
typeof(SqlTemplatesBenchmark),
|
||||
|
||||
});
|
||||
switcher.Run(args);
|
||||
|
||||
82
src/Umbraco.Tests.Benchmarks/SqlTemplatesBenchmark.cs
Normal file
82
src/Umbraco.Tests.Benchmarks/SqlTemplatesBenchmark.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Tests.Persistence.NPocoTests;
|
||||
|
||||
namespace Umbraco.Tests.Benchmarks
|
||||
{
|
||||
// seeing this kind of results - templates are gooood
|
||||
//
|
||||
// Method | Mean | Error | StdDev | Scaled | Gen 0 | Allocated |
|
||||
// ---------------- |-----------:|----------:|----------:|-------:|---------:|----------:|
|
||||
// WithoutTemplate | 2,895.0 us | 64.873 us | 99.068 us | 1.00 | 183.5938 | 762.23 KB |
|
||||
// WithTemplate | 263.2 us | 4.581 us | 4.285 us | 0.09 | 50.2930 | 207.13 KB |
|
||||
//
|
||||
// though the difference might not be so obvious in case of WhereIn which requires parsing
|
||||
|
||||
[Config(typeof(Config))]
|
||||
public class SqlTemplatesBenchmark
|
||||
{
|
||||
private class Config : ManualConfig
|
||||
{
|
||||
public Config()
|
||||
{
|
||||
Add(new MemoryDiagnoser());
|
||||
}
|
||||
}
|
||||
|
||||
public SqlTemplatesBenchmark()
|
||||
{
|
||||
var mappers = new NPoco.MapperCollection { new PocoMapper() };
|
||||
var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init());
|
||||
|
||||
SqlContext = new SqlContext(new SqlCeSyntaxProvider(), DatabaseType.SQLCe, factory);
|
||||
SqlTemplates = new SqlTemplates(SqlContext);
|
||||
}
|
||||
|
||||
private ISqlContext SqlContext { get; }
|
||||
private SqlTemplates SqlTemplates { get; }
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void WithoutTemplate()
|
||||
{
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
var sql = Sql.BuilderFor(SqlContext)
|
||||
.Select<NPocoFetchTests.Thing1Dto>()
|
||||
.From<NPocoFetchTests.Thing1Dto>()
|
||||
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == "yada");
|
||||
|
||||
var sqlString = sql.SQL; // force-build the SQL
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void WithTemplate()
|
||||
{
|
||||
SqlTemplates.Clear();
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
var template = SqlTemplates.Get("test", s => s
|
||||
.Select<NPocoFetchTests.Thing1Dto>()
|
||||
.From<NPocoFetchTests.Thing1Dto>()
|
||||
.Where<NPocoFetchTests.Thing1Dto>(x => x.Name == SqlTemplate.Arg<string>("name")));
|
||||
|
||||
var sql = template.Sql(new { name = "yada" });
|
||||
|
||||
var sqlString = sql.SQL; // force-build the SQL
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -170,6 +170,7 @@
|
||||
<Compile Include="BulkInsertBenchmarks.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SqlTemplatesBenchmark.cs" />
|
||||
<Compile Include="XmlBenchmarks.cs" />
|
||||
<Compile Include="XmlPublishedContentInitBenchmarks.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,7 +3,9 @@ using Moq;
|
||||
using NPoco;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
{
|
||||
@@ -11,7 +13,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
public class NPocoSqlTemplateTests
|
||||
{
|
||||
[Test]
|
||||
public void TestSqlTemplates()
|
||||
public void SqlTemplates()
|
||||
{
|
||||
var sqlContext = new SqlContext(new SqlCeSyntaxProvider(), DatabaseType.SQLCe, Mock.Of<IPocoDataFactory>());
|
||||
var sqlTemplates = new SqlTemplates(sqlContext);
|
||||
@@ -23,7 +25,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
var sql = sqlTemplates.Get("xxx", s => s
|
||||
.SelectAll()
|
||||
.From("zbThing1")
|
||||
.Where("id=@id", new { id = "id" })).SqlNamed(new { id = 1 });
|
||||
.Where("id=@id", new { id = "id" })).Sql(new { id = 1 });
|
||||
|
||||
sql.WriteToConsole();
|
||||
|
||||
@@ -31,9 +33,171 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
|
||||
sql2.WriteToConsole();
|
||||
|
||||
var sql3 = sqlTemplates.Get("xxx", x => throw new InvalidOperationException("Should be cached.")).SqlNamed(new { id = 1 });
|
||||
var sql3 = sqlTemplates.Get("xxx", x => throw new InvalidOperationException("Should be cached.")).Sql(new { id = 1 });
|
||||
|
||||
sql3.WriteToConsole();
|
||||
}
|
||||
|
||||
[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 == "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 == "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 }).WriteToConsole());
|
||||
Assert.Throws<InvalidOperationException>(() => template.Sql(new { value = 123, xvalue = 456 }).WriteToConsole());
|
||||
|
||||
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 }).WriteToConsole());
|
||||
Assert.Throws<InvalidOperationException>(() => template.Sql(new { i = 123, j = 456 }).WriteToConsole());
|
||||
|
||||
// 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"))
|
||||
.Append("AND whatever=@0", "j") // instead, directly name the argument
|
||||
.Append("AND whatever=@0", "k") // same
|
||||
);
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
//now add the user props
|
||||
contentProps.AddRange(currProps);
|
||||
|
||||
|
||||
//callback
|
||||
onGenericPropertiesMapped?.Invoke(contentProps);
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
var listViewConfig = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals);
|
||||
//add the entity type to the config
|
||||
listViewConfig["entityType"] = entityType;
|
||||
|
||||
|
||||
//Override Tab Label if tabName is provided
|
||||
if (listViewConfig.ContainsKey("tabName"))
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[PreValueField("tabName", "Tab Name", "textstring", Description = "The name of the listview tab (default if empty: 'Child Items')")]
|
||||
public int TabName { get; set; }
|
||||
|
||||
|
||||
[PreValueField("displayAtTabNumber", "Display At Tab Number", "number", Description = "Which tab position that the list of child items will be displayed")]
|
||||
public int DisplayAtTabNumber { get; set; }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Umbraco.Web._Legacy.Actions;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
/// <summary>
|
||||
@@ -24,23 +24,23 @@ namespace Umbraco.Web.Trees
|
||||
[CoreTree]
|
||||
public class ContentBlueprintTreeController : TreeController
|
||||
{
|
||||
|
||||
|
||||
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
|
||||
{
|
||||
var root = base.CreateRootNode(queryStrings);
|
||||
|
||||
|
||||
//this will load in a custom UI instead of the dashboard for the root node
|
||||
root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Settings, Constants.Trees.ContentBlueprints, "intro");
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var nodes = new TreeNodeCollection();
|
||||
|
||||
|
||||
//get all blueprints
|
||||
var entities = Services.EntityService.GetChildren(Constants.System.Root, UmbracoObjectTypes.DocumentBlueprint).ToArray();
|
||||
|
||||
|
||||
//check if we're rendering the root in which case we'll render the content types that have blueprints
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
@@ -48,12 +48,12 @@ namespace Umbraco.Web.Trees
|
||||
var contentTypeAliases = entities.Select(x => ((UmbracoEntity) x).ContentTypeAlias).Distinct();
|
||||
//get the ids
|
||||
var contentTypeIds = Services.ContentTypeService.GetAllContentTypeIds(contentTypeAliases.ToArray()).ToArray();
|
||||
|
||||
|
||||
//now get the entities ... it's a bit round about but still smaller queries than getting all document types
|
||||
var docTypeEntities = contentTypeIds.Length == 0
|
||||
? new IUmbracoEntity[0]
|
||||
: Services.EntityService.GetAll(UmbracoObjectTypes.DocumentType, contentTypeIds).ToArray();
|
||||
|
||||
|
||||
nodes.AddRange(docTypeEntities
|
||||
.Select(entity =>
|
||||
{
|
||||
@@ -64,15 +64,15 @@ namespace Umbraco.Web.Trees
|
||||
treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);";
|
||||
return treeNode;
|
||||
}));
|
||||
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
var intId = id.TryConvertTo<int>();
|
||||
//Get the content type
|
||||
var ct = Services.ContentTypeService.Get(intId.Result);
|
||||
if (ct == null) return nodes;
|
||||
|
||||
|
||||
var blueprintsForDocType = entities.Where(x => ct.Alias == ((UmbracoEntity) x).ContentTypeAlias);
|
||||
nodes.AddRange(blueprintsForDocType
|
||||
.Select(entity =>
|
||||
@@ -81,14 +81,14 @@ namespace Umbraco.Web.Trees
|
||||
treeNode.Path = $"-1,{ct.Id},{entity.Id}";
|
||||
return treeNode;
|
||||
}));
|
||||
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
// root actions
|
||||
@@ -103,16 +103,16 @@ namespace Umbraco.Web.Trees
|
||||
var ct = Services.ContentTypeService.Get(cte.Id);
|
||||
var createItem = menu.Items.Add<ActionCreateBlueprintFromContent>(Services.TextService.Localize($"actions/{ActionCreateBlueprintFromContent.Instance.Alias}"));
|
||||
createItem.NavigateToRoute("/settings/contentBlueprints/edit/-1?create=true&doctype=" + ct.Alias);
|
||||
|
||||
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize($"actions/{ActionRefresh.Instance.Alias}"), true);
|
||||
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}"));
|
||||
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user