Merge remote-tracking branch 'origin/v10/dev' into v10/feature/nullable-reference-types-in-Umbraco-Core

# Conflicts:
#	build/build.ps1
#	src/Umbraco.Core/Configuration/ConfigConnectionString.cs
#	src/Umbraco.Core/Configuration/Models/ConnectionStrings.cs
#	src/Umbraco.Core/Install/InstallSteps/TelemetryIdentifierStep.cs
#	src/Umbraco.Core/Models/ContentType.cs
#	src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
#	tests/Umbraco.Tests.AcceptanceTest/package.json
This commit is contained in:
Nikolaj Geisle
2022-03-16 13:00:38 +01:00
480 changed files with 11569 additions and 6721 deletions

View File

@@ -92,8 +92,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
var sqlClause = Sql()
.SelectAll()
.From<PropertyTypeGroupDto>()
.RightJoin<PropertyTypeDto>()
.From<PropertyTypeDto>()
.LeftJoin<PropertyTypeGroupDto>()
.On<PropertyTypeGroupDto, PropertyTypeDto>(left => left.Id, right => right.PropertyTypeGroupId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.NodeId);

View File

@@ -757,7 +757,7 @@ AND umbracoNode.id <> @id",
//now we need to insert names into these 2 tables based on the invariant data
//insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang
var cols = Sql().Columns<ContentVersionCultureVariationDto>(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId);
var cols = Sql().ColumnsForInsert<ContentVersionCultureVariationDto>(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId);
sqlSelect = Sql().Select<ContentVersionDto>(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate)
.Append($", {defaultLanguageId}") //default language ID
.From<ContentVersionDto>()
@@ -768,7 +768,7 @@ AND umbracoNode.id <> @id",
Database.Execute(sqlInsert);
//insert rows into the documentCultureVariation table
cols = Sql().Columns<DocumentCultureVariationDto>(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId);
cols = Sql().ColumnsForInsert<DocumentCultureVariationDto>(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId);
sqlSelect = Sql().Select<DocumentDto>(x => x.NodeId, x => x.Edited, x => x.Published)
.AndSelect<NodeDto>(x => x.Text)
.Append($", 1, {defaultLanguageId}") //make Available + default language ID
@@ -856,7 +856,7 @@ AND umbracoNode.id <> @id",
.WhereNull<TagDto>(x => x.Id, "xtags") // ie, not exists
.Where<TagDto>(x => x.LanguageId.SqlNullableEquals(sourceLanguageId, -1));
var cols = Sql().Columns<TagDto>(x => x.Text, x => x.Group, x => x.LanguageId);
var cols = Sql().ColumnsForInsert<TagDto>(x => x.Text, x => x.Group, x => x.LanguageId);
var sqlInsertTags = Sql($"INSERT INTO {TagDto.TableName} ({cols})").Append(sqlSelectTagsToInsert);
Database.Execute(sqlInsertTags);
@@ -884,7 +884,7 @@ AND umbracoNode.id <> @id",
.Where<TagDto>(x => x.LanguageId.SqlNullableEquals(sourceLanguageId, -1))
.WhereIn<TagRelationshipDto>(x => x.PropertyTypeId, propertyTypeIds);
var relationColumnsToInsert = Sql().Columns<TagRelationshipDto>(x => x.NodeId, x => x.PropertyTypeId, x => x.TagId);
var relationColumnsToInsert = Sql().ColumnsForInsert<TagRelationshipDto>(x => x.NodeId, x => x.PropertyTypeId, x => x.TagId);
var sqlInsertRelations = Sql($"INSERT INTO {TagRelationshipDto.TableName} ({relationColumnsToInsert})").Append(sqlSelectRelationsToInsert);
Database.Execute(sqlInsertRelations);
@@ -972,7 +972,7 @@ AND umbracoNode.id <> @id",
//now insert all property data into the target language that exists under the source language
var targetLanguageIdS = targetLanguageId.HasValue ? targetLanguageId.ToString() : "NULL";
var cols = Sql().Columns<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue, x => x.LanguageId);
var cols = Sql().ColumnsForInsert<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue, x => x.LanguageId);
var sqlSelectData = Sql().Select<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue)
.Append(", " + targetLanguageIdS) //default language ID
.From<PropertyDataDto>();

View File

@@ -39,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
private readonly IMacroService _macroService;
private readonly IContentTypeService _contentTypeService;
private readonly string _tempFolderPath;
private readonly string _mediaFolderPath;
private readonly string _createdPackagesFolderPath;
/// <summary>
/// Initializes a new instance of the <see cref="CreatedPackageSchemaRepository"/> class.
@@ -76,9 +76,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
_macroService = macroService;
_contentTypeService = contentTypeService;
_xmlParser = new PackageDefinitionXmlParser();
_mediaFolderPath = mediaFolderPath ?? Path.Combine(globalSettings.Value.UmbracoMediaPhysicalRootPath, Constants.SystemDirectories.CreatedPackages);
_tempFolderPath =
tempFolderPath ?? Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "PackageFiles";
_createdPackagesFolderPath = mediaFolderPath ?? Constants.SystemDirectories.CreatedPackages;
_tempFolderPath = tempFolderPath ?? Constants.SystemDirectories.TempData + "/PackageFiles";
}
public IEnumerable<PackageDefinition> GetAll()
@@ -137,7 +136,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
.Delete<CreatedPackageSchemaDto>()
.Where<CreatedPackageSchemaDto>(x => x.Id == id);
_umbracoDatabase.Delete<CreatedPackageSchemaDto>(query);
_umbracoDatabase.Execute(query);
}
public bool SavePackage(PackageDefinition definition)
@@ -168,10 +167,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
};
// Set the ids, we have to save in database first to get the Id
definition.PackageId = dto.PackageId;
var result = _umbracoDatabase.Insert(dto);
var decimalResult = result.SafeCast<decimal>();
definition.Id = decimal.ToInt32(decimalResult);
_umbracoDatabase.Insert(dto);
definition.Id = dto.Id;
}
// Save snapshot locally, we do this to the updated packagePath
@@ -192,17 +189,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
public string ExportPackage(PackageDefinition definition)
{
// Ensure it's valid
ValidatePackage(definition);
// Create a folder for building this package
var temporaryPath =
_hostingEnvironment.MapPathContentRoot(_tempFolderPath.EnsureEndsWith('/') + Guid.NewGuid());
if (Directory.Exists(temporaryPath) == false)
{
Directory.CreateDirectory(temporaryPath);
}
var temporaryPath = _hostingEnvironment.MapPathContentRoot(Path.Combine(_tempFolderPath, Guid.NewGuid().ToString()));
Directory.CreateDirectory(temporaryPath);
try
{
@@ -218,8 +210,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
PackageTemplates(definition, root);
PackageStylesheets(definition, root);
PackageStaticFiles(definition.Scripts, root, "Scripts", "Script", _fileSystems.ScriptsFileSystem);
PackageStaticFiles(definition.PartialViews, root, "PartialViews", "View",
_fileSystems.PartialViewsFileSystem);
PackageStaticFiles(definition.PartialViews, root, "PartialViews", "View", _fileSystems.PartialViewsFileSystem);
PackageMacros(definition, root);
PackageDictionaryItems(definition, root);
PackageLanguages(definition, root);
@@ -265,27 +256,25 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
}
}
var directoryName =
_hostingEnvironment.MapPathWebRoot(
Path.Combine(_mediaFolderPath, definition.Name.Replace(' ', '_')));
if (Directory.Exists(directoryName) == false)
{
Directory.CreateDirectory(directoryName);
}
var directoryName = _hostingEnvironment.MapPathContentRoot(Path.Combine(_createdPackagesFolderPath, definition.Name.Replace(' ', '_')));
Directory.CreateDirectory(directoryName);
var finalPackagePath = Path.Combine(directoryName, fileName);
if (File.Exists(finalPackagePath))
// Clean existing files
foreach (var packagePath in new[]
{
File.Delete(finalPackagePath);
}
if (File.Exists(finalPackagePath.Replace("zip", "xml")))
{
File.Delete(finalPackagePath.Replace("zip", "xml"));
definition.PackagePath,
finalPackagePath
})
{
if (File.Exists(packagePath))
{
File.Delete(packagePath);
}
}
// Move to final package path
File.Move(tempPackagePath, finalPackagePath);
definition.PackagePath = finalPackagePath;

View File

@@ -356,7 +356,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
.On<NodeDto, DocumentCultureVariationDto, LanguageDto>((node, dcv, lang) => node.NodeId == dcv.NodeId && lang.Id == dcv.LanguageId, aliasRight: "dcv")
// for selected nodes
.WhereIn<NodeDto>(x => x.NodeId, ids);
.WhereIn<NodeDto>(x => x.NodeId, ids)
.OrderBy<LanguageDto>(x => x.Id);
}
// gets the full sql for a given object type and a given unique id

View File

@@ -296,7 +296,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
=> forUpdate ? Sql()
.Select<ExternalLoginTokenDto>(r => r.Select(x => x.ExternalLoginDto))
.From<ExternalLoginTokenDto>()
.Append(" WITH (UPDLOCK)") // ensure these table values are locked for updates, the ForUpdate ext method does not work here
.AppendForUpdateHint() // ensure these table values are locked for updates, the ForUpdate ext method does not work here
.InnerJoin<ExternalLoginDto>()
.On<ExternalLoginTokenDto, ExternalLoginDto>(x => x.ExternalLoginId, x => x.Id)
: Sql()

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using NPoco;
using Umbraco.Cms.Core;
@@ -171,6 +172,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
}
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
{
return GetPagedParentEntitiesByChildId(childId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes);
}
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes)
{
// var contentObjectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member }
// we could pass in the contentObjectTypes so that the entity repository sql is configured to do full entity lookups so that we get the full data
@@ -184,10 +190,36 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
sql.Where<RelationDto>(rel => rel.ChildId == childId);
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ParentId == childId || node.NodeId != childId);
if (relationTypes != null && relationTypes.Any())
{
sql.WhereIn<RelationDto>(rel => rel.RelationType, relationTypes);
}
});
}
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildIds(int[] childIds, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes)
{
return _entityRepository.GetPagedResultsByQuery(Query<IUmbracoEntity>(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql =>
{
SqlJoinRelations(sql);
sql.WhereIn<RelationDto>(rel => rel.ChildId, childIds);
sql.WhereAny(s => s.WhereIn<RelationDto>(rel => rel.ParentId, childIds), s => s.WhereNotIn<NodeDto>(node => node.NodeId, childIds));
if (relationTypes != null && relationTypes.Any())
{
sql.WhereIn<RelationDto>(rel => rel.RelationType, relationTypes);
}
});
}
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
{
return GetPagedChildEntitiesByParentId(parentId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes);
}
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes)
{
// var contentObjectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member }
// we could pass in the contentObjectTypes so that the entity repository sql is configured to do full entity lookups so that we get the full data
@@ -201,9 +233,29 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
sql.Where<RelationDto>(rel => rel.ParentId == parentId);
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ChildId == parentId || node.NodeId != parentId);
if (relationTypes != null && relationTypes.Any())
{
sql.WhereIn<RelationDto>(rel => rel.RelationType, relationTypes);
}
});
}
public IEnumerable<IUmbracoEntity> GetPagedEntitiesForItemsInRelation(int[] itemIds, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
{
return _entityRepository.GetPagedResultsByQuery(Query<IUmbracoEntity>(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql =>
{
SqlJoinRelations(sql);
sql.WhereIn<RelationDto>(rel => rel.ChildId, itemIds);
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ChildId == node.NodeId);
sql.Where<RelationTypeDto>(type => type.IsDependency);
});
}
public void Save(IEnumerable<IRelation> relations)
{
foreach (var hasIdentityGroup in relations.GroupBy(r => r.HasIdentity))
@@ -313,8 +365,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
public void DeleteByParent(int parentId, params string[] relationTypeAliases)
{
if (Database.DatabaseType.IsSqlCe())
// HACK: SQLite - hard to replace this without provider specific repositories/another ORM.
if (Database.DatabaseType.IsSqlite())
{
var query = Sql().Append(@"delete from umbracoRelation");
var subQuery = Sql().Select<RelationDto>(x => x.Id)
.From<RelationDto>()
.InnerJoin<RelationTypeDto>().On<RelationDto, RelationTypeDto>(x => x.RelationType, x => x.Id)
@@ -325,8 +380,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
subQuery.WhereIn<RelationTypeDto>(x => x.Alias, relationTypeAliases);
}
Database.Execute(Sql().Delete<RelationDto>().WhereIn<RelationDto>(x => x.Id, subQuery));
var fullQuery = query.WhereIn<RelationDto>(x => x.Id, subQuery);
Database.Execute(fullQuery);
}
else
{
@@ -399,4 +455,42 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
sql.OrderByDescending(orderBy);
}
}
internal class RelationItemDto
{
[Column(Name = "nodeId")]
public int ChildNodeId { get; set; }
[Column(Name = "nodeKey")]
public Guid ChildNodeKey { get; set; }
[Column(Name = "nodeName")]
public string ChildNodeName { get; set; }
[Column(Name = "nodeObjectType")]
public Guid ChildNodeObjectType { get; set; }
[Column(Name = "contentTypeIcon")]
public string ChildContentTypeIcon { get; set; }
[Column(Name = "contentTypeAlias")]
public string ChildContentTypeAlias { get; set; }
[Column(Name = "contentTypeName")]
public string ChildContentTypeName { get; set; }
[Column(Name = "relationTypeName")]
public string RelationTypeName { get; set; }
[Column(Name = "relationTypeAlias")]
public string RelationTypeAlias { get; set; }
[Column(Name = "relationTypeIsDependency")]
public bool RelationTypeIsDependency { get; set; }
[Column(Name = "relationTypeIsBidirectional")]
public bool RelationTypeIsBidirectional { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
@@ -51,7 +51,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
}
}
// no suffix - name without suffix does NOT exist, AND name with suffix does NOT exist
// no suffix - name without suffix does NOT exist - we can just use the name without suffix.
if (!model.Suffix.HasValue && !items.SimpleNameExists(model.Text))
{
model.Suffix = StructuredName.NO_SUFFIX;
return model.FullName;
}
// suffix - name with suffix does NOT exist
// We can just return the full name as it is as there's no conflict.
if (model.Suffix.HasValue && !items.SimpleNameExists(model.FullName))
{
return model.FullName;
}
// no suffix - name without suffix does NOT exist, AND name with suffix does NOT exist
if (!model.Suffix.HasValue && !items.SimpleNameExists(model.Text) && !items.SuffixedNameExists())
{
model.Suffix = StructuredName.NO_SUFFIX;
@@ -163,7 +178,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
internal static readonly uint? NO_SUFFIX = default;
internal string Text { get; set; }
internal uint ? Suffix { get; set; }
internal uint? Suffix { get; set; }
public string FullName
{
get

View File

@@ -210,7 +210,16 @@ WHERE r.tagId IS NULL";
sql.Append(" UNION ");
}
sql.Append("SELECT N'");
// HACK: SQLite (or rather SQL server setup was a hack)
if (SqlContext.DatabaseType.IsSqlServer())
{
sql.Append("SELECT N'");
}
else
{
sql.Append("SELECT '");
}
sql.Append(SqlSyntax.EscapeString(tag.Text));
sql.Append("' AS tag, '");
sql.Append(SqlSyntax.EscapeString(tag.Group));

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
internal class TrackedReferencesRepository : ITrackedReferencesRepository
{
private readonly IScopeAccessor _scopeAccessor;
public TrackedReferencesRepository(IScopeAccessor scopeAccessor)
{
_scopeAccessor = scopeAccessor;
}
public IEnumerable<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize,
bool filterMustBeIsDependency, out long totalRecords)
{
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
"[pn].[id] as nodeId",
"[pn].[uniqueId] as nodeKey",
"[pn].[text] as nodeName",
"[pn].[nodeObjectType] as nodeObjectType",
"[ct].[icon] as contentTypeIcon",
"[ct].[alias] as contentTypeAlias",
"[ctn].[text] as contentTypeName",
"[umbracoRelationType].[alias] as relationTypeAlias",
"[umbracoRelationType].[name] as relationTypeName",
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
.From<RelationDto>("r")
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c")
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn");
if (ids.Any())
{
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "pn");
}
if (filterMustBeIsDependency)
{
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
}
// Ordering is required for paging
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
return pagedResult.Items.Select(MapDtoToEntity);
}
public IEnumerable<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency,
out long totalRecords)
{
var syntax = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax;
// Gets the path of the parent with ",%" added
var subsubQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql()
.Select(syntax.GetConcat("[node].[path]", "',%'"))
.From<NodeDto>("node")
.Where<NodeDto>(x => x.NodeId == parentId, "node");
// Gets the descendants of the parent node
Sql<ISqlContext> subQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql()
.Select<NodeDto>(x => x.NodeId)
.From<NodeDto>()
.WhereLike<NodeDto>(x => x.Path, subsubQuery);
// Get all relations where parent is in the sub query
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
"[pn].[id] as nodeId",
"[pn].[uniqueId] as nodeKey",
"[pn].[text] as nodeName",
"[pn].[nodeObjectType] as nodeObjectType",
"[ct].[icon] as contentTypeIcon",
"[ct].[alias] as contentTypeAlias",
"[ctn].[text] as contentTypeName",
"[umbracoRelationType].[alias] as relationTypeAlias",
"[umbracoRelationType].[name] as relationTypeName",
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
.From<RelationDto>("r")
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c")
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn")
.WhereIn((System.Linq.Expressions.Expression<Func<NodeDto, object>>)(x => x.NodeId), subQuery, "pn");
if (filterMustBeIsDependency)
{
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
}
// Ordering is required for paging
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
return pagedResult.Items.Select(MapDtoToEntity);
}
public IEnumerable<RelationItem> GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
{
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
"[cn].[id] as nodeId",
"[cn].[uniqueId] as nodeKey",
"[cn].[text] as nodeName",
"[cn].[nodeObjectType] as nodeObjectType",
"[ct].[icon] as contentTypeIcon",
"[ct].[alias] as contentTypeAlias",
"[ctn].[text] as contentTypeName",
"[umbracoRelationType].[alias] as relationTypeAlias",
"[umbracoRelationType].[name] as relationTypeName",
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
.From<RelationDto>("r")
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"cn", aliasRight:"c")
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn");
if (ids.Any())
{
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "pn");
}
if (filterMustBeIsDependency)
{
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
}
// Ordering is required for paging
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
return pagedResult.Items.Select(MapDtoToEntity);
}
private RelationItem MapDtoToEntity(RelationItemDto dto)
{
var type = ObjectTypes.GetUdiType(dto.ChildNodeObjectType);
return new RelationItem()
{
NodeId = dto.ChildNodeId,
NodeKey = dto.ChildNodeKey,
NodeType = ObjectTypes.GetUdiType(dto.ChildNodeObjectType),
NodeName = dto.ChildNodeName,
RelationTypeName = dto.RelationTypeName,
RelationTypeIsBidirectional = dto.RelationTypeIsBidirectional,
RelationTypeIsDependency = dto.RelationTypeIsDependency,
ContentTypeAlias = dto.ChildContentTypeAlias,
ContentTypeIcon = dto.ChildContentTypeIcon,
ContentTypeName = dto.ChildContentTypeName,
};
}
}
}

View File

@@ -225,10 +225,10 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0
// that query is going to run a *lot*, make it a template
var t = SqlContext.Templates.Get("Umbraco.Core.UserRepository.ValidateLoginSession", s => s
.Select<UserLoginDto>()
.SelectTop(1)
.From<UserLoginDto>()
.Where<UserLoginDto>(x => x.SessionId == SqlTemplate.Arg<Guid>("sessionId"))
.ForUpdate());
.ForUpdate()
.SelectTop(1)); // Stick at end, SQL server syntax provider will insert at start of query after "select ", but sqlite will append limit to end.
var sql = t.Sql(sessionId);