2020-12-09 22:43:49 +11:00
using System ;
2017-12-07 16:45:25 +01:00
using System.Collections.Generic ;
using System.Linq ;
using NPoco ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core ;
using Umbraco.Cms.Core.Cache ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.Entities ;
using Umbraco.Cms.Core.Persistence.Querying ;
2021-02-12 13:36:50 +01:00
using Umbraco.Cms.Core.Persistence.Repositories ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Services ;
2021-02-12 13:36:50 +01:00
using Umbraco.Cms.Infrastructure.Persistence.Dtos ;
using Umbraco.Cms.Infrastructure.Persistence.Querying ;
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax ;
2022-01-13 17:44:11 +00:00
using Umbraco.Cms.Infrastructure.Scoping ;
2021-02-09 11:26:22 +01:00
using Umbraco.Extensions ;
2021-02-09 10:22:42 +01:00
using static Umbraco . Cms . Core . Persistence . SqlExtensionsStatics ;
2017-12-07 16:45:25 +01:00
2021-02-12 13:36:50 +01:00
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
2017-12-07 16:45:25 +01:00
{
/// <summary>
2018-01-15 11:32:30 +01:00
/// Represents the EntityRepository used to query entity objects.
2017-12-07 16:45:25 +01:00
/// </summary>
/// <remarks>
2018-01-15 11:32:30 +01:00
/// <para>Limited to objects that have a corresponding node (in umbracoNode table).</para>
/// <para>Returns <see cref="IEntitySlim"/> objects, i.e. lightweight representation of entities.</para>
2017-12-07 16:45:25 +01:00
/// </remarks>
2022-01-13 23:22:19 +00:00
internal class EntityRepository : RepositoryBase , IEntityRepositoryExtended
2017-12-07 16:45:25 +01:00
{
2020-12-09 22:43:49 +11:00
public EntityRepository ( IScopeAccessor scopeAccessor , AppCaches appCaches )
: base ( scopeAccessor , appCaches )
2017-12-07 16:45:25 +01:00
{
}
#region Repository
2020-12-09 22:43:49 +11:00
2018-01-15 11:32:30 +01:00
public IEnumerable < IEntitySlim > GetPagedResultsByQuery ( IQuery < IUmbracoEntity > query , Guid objectType , long pageIndex , int pageSize , out long totalRecords ,
2022-02-24 09:24:56 +01:00
IQuery < IUmbracoEntity > ? filter , Ordering ? ordering )
2017-12-07 16:45:25 +01:00
{
2019-11-05 15:05:51 +11:00
return GetPagedResultsByQuery ( query , new [ ] { objectType } , pageIndex , pageSize , out totalRecords , filter , ordering ) ;
}
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
// get a page of entities
public IEnumerable < IEntitySlim > GetPagedResultsByQuery ( IQuery < IUmbracoEntity > query , Guid [ ] objectTypes , long pageIndex , int pageSize , out long totalRecords ,
2022-02-24 09:24:56 +01:00
IQuery < IUmbracoEntity > ? filter , Ordering ? ordering , Action < Sql < ISqlContext > > ? sqlCustomization = null )
2019-11-05 15:05:51 +11:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectTypes . Any ( objectType = > objectType = = Cms . Core . Constants . ObjectTypes . Document | | objectType = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ) ;
var isMedia = objectTypes . Any ( objectType = > objectType = = Cms . Core . Constants . ObjectTypes . Media ) ;
var isMember = objectTypes . Any ( objectType = > objectType = = Cms . Core . Constants . ObjectTypes . Member ) ;
2017-12-07 16:45:25 +01:00
2020-12-09 22:43:49 +11:00
Sql < ISqlContext > sql = GetBaseWhere ( isContent , isMedia , isMember , false , s = >
2017-12-07 16:45:25 +01:00
{
2019-11-15 14:14:09 +11:00
sqlCustomization ? . Invoke ( s ) ;
2019-11-06 12:43:10 +11:00
if ( filter ! = null )
{
2020-12-09 22:43:49 +11:00
foreach ( Tuple < string , object [ ] > filterClause in filter . GetWhereClauses ( ) )
{
2019-11-06 12:43:10 +11:00
s . Where ( filterClause . Item1 , filterClause . Item2 ) ;
2020-12-09 22:43:49 +11:00
}
2019-11-06 12:43:10 +11:00
}
2019-11-05 15:05:51 +11:00
} , objectTypes ) ;
2017-12-07 16:45:25 +01:00
2018-12-21 13:15:46 +11:00
ordering = ordering ? ? Ordering . ByDefault ( ) ;
2017-12-07 16:45:25 +01:00
var translator = new SqlTranslator < IUmbracoEntity > ( sql , query ) ;
sql = translator . Translate ( ) ;
2019-10-14 14:34:33 +02:00
sql = AddGroupBy ( isContent , isMedia , isMember , sql , ordering . IsEmpty ) ;
2018-12-21 13:15:46 +11:00
if ( ! ordering . IsEmpty )
{
// apply ordering
ApplyOrdering ( ref sql , ordering ) ;
}
2019-01-26 09:42:14 -05:00
// TODO: we should be able to do sql = sql.OrderBy(x => Alias(x.NodeId, "NodeId")); but we can't because the OrderBy extension don't support Alias currently
2020-12-09 22:43:49 +11:00
// no matter what we always must have node id ordered at the end
2018-12-21 13:15:46 +11:00
sql = ordering . Direction = = Direction . Ascending ? sql . OrderBy ( "NodeId" ) : sql . OrderByDescending ( "NodeId" ) ;
2017-12-07 16:45:25 +01:00
2019-02-09 15:41:30 +01:00
// for content we must query for ContentEntityDto entities to produce the correct culture variant entity names
var pageIndexToFetch = pageIndex + 1 ;
IEnumerable < BaseDto > dtos ;
2019-11-05 15:05:51 +11:00
var page = Database . Page < GenericContentEntityDto > ( pageIndexToFetch , pageSize , sql ) ;
dtos = page . Items ;
totalRecords = page . TotalItems ;
2019-02-09 15:41:30 +01:00
2019-11-05 15:05:51 +11:00
var entities = dtos . Select ( BuildEntity ) . ToArray ( ) ;
2019-02-08 08:50:57 +01:00
2019-11-05 15:05:51 +11:00
BuildVariants ( entities . OfType < DocumentEntitySlim > ( ) ) ;
2018-04-20 13:12:55 +10:00
2017-12-07 16:45:25 +01:00
return entities ;
}
2022-02-24 09:24:56 +01:00
public IEntitySlim ? Get ( Guid key )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( false , false , false , false , key ) ;
2017-12-07 16:45:25 +01:00
var dto = Database . FirstOrDefault < BaseDto > ( sql ) ;
2019-11-05 15:05:51 +11:00
return dto = = null ? null : BuildEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
}
2018-12-21 13:15:46 +11:00
2022-02-24 09:24:56 +01:00
private IEntitySlim ? GetEntity ( Sql < ISqlContext > sql , bool isContent , bool isMedia , bool isMember )
2017-12-07 16:45:25 +01:00
{
2020-12-09 22:43:49 +11:00
// isContent is going to return a 1:M result now with the variants so we need to do different things
2018-04-20 16:07:34 +10:00
if ( isContent )
{
2019-11-05 15:05:51 +11:00
var cdtos = Database . Fetch < DocumentEntityDto > ( sql ) ;
2018-09-25 10:55:33 +02:00
return cdtos . Count = = 0 ? null : BuildVariants ( BuildDocumentEntity ( cdtos [ 0 ] ) ) ;
2018-04-20 16:07:34 +10:00
}
2017-12-07 16:45:25 +01:00
2019-06-13 23:39:36 +10:00
var dto = isMedia
? Database . FirstOrDefault < MediaEntityDto > ( sql )
: Database . FirstOrDefault < BaseDto > ( sql ) ;
2018-04-24 17:59:26 +02:00
if ( dto = = null ) return null ;
2018-04-20 16:07:34 +10:00
2019-11-05 15:05:51 +11:00
var entity = BuildEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
2018-04-24 17:59:26 +02:00
return entity ;
}
2022-02-24 09:24:56 +01:00
public IEntitySlim ? Get ( Guid key , Guid objectTypeId )
2018-04-24 17:59:26 +02:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectTypeId = = Cms . Core . Constants . ObjectTypes . Document | | objectTypeId = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectTypeId = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectTypeId = = Cms . Core . Constants . ObjectTypes . Member ;
2018-04-24 17:59:26 +02:00
2019-10-14 14:34:33 +02:00
var sql = GetFullSqlForEntityType ( isContent , isMedia , isMember , objectTypeId , key ) ;
return GetEntity ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
2022-02-24 09:24:56 +01:00
public IEntitySlim ? Get ( int id )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( false , false , false , false , id ) ;
2017-12-07 16:45:25 +01:00
var dto = Database . FirstOrDefault < BaseDto > ( sql ) ;
2019-11-05 15:05:51 +11:00
return dto = = null ? null : BuildEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
}
2022-02-24 09:24:56 +01:00
public IEntitySlim ? Get ( int id , Guid objectTypeId )
2017-12-07 16:45:25 +01:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectTypeId = = Cms . Core . Constants . ObjectTypes . Document | | objectTypeId = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectTypeId = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectTypeId = = Cms . Core . Constants . ObjectTypes . Member ;
2017-12-07 16:45:25 +01:00
2019-10-14 14:34:33 +02:00
var sql = GetFullSqlForEntityType ( isContent , isMedia , isMember , objectTypeId , id ) ;
return GetEntity ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetAll ( Guid objectType , params int [ ] ids )
2017-12-07 16:45:25 +01:00
{
return ids . Length > 0
? PerformGetAll ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . NodeId , ids . Distinct ( ) ) )
: PerformGetAll ( objectType ) ;
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetAll ( Guid objectType , params Guid [ ] keys )
2017-12-07 16:45:25 +01:00
{
return keys . Length > 0
? PerformGetAll ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . UniqueId , keys . Distinct ( ) ) )
: PerformGetAll ( objectType ) ;
}
2019-10-14 14:34:33 +02:00
private IEnumerable < IEntitySlim > GetEntities ( Sql < ISqlContext > sql , bool isContent , bool isMedia , bool isMember )
2017-12-07 16:45:25 +01:00
{
2020-12-09 22:43:49 +11:00
// isContent is going to return a 1:M result now with the variants so we need to do different things
2018-04-20 16:07:34 +10:00
if ( isContent )
{
2019-11-05 15:05:51 +11:00
var cdtos = Database . Fetch < DocumentEntityDto > ( sql ) ;
2018-09-21 15:49:37 +10:00
2018-04-24 17:59:26 +02:00
return cdtos . Count = = 0
? Enumerable . Empty < IEntitySlim > ( )
2018-09-25 10:55:33 +02:00
: BuildVariants ( cdtos . Select ( BuildDocumentEntity ) ) . ToList ( ) ;
2018-04-20 16:07:34 +10:00
}
2017-12-07 16:45:25 +01:00
2019-06-13 23:39:36 +10:00
var dtos = isMedia
? ( IEnumerable < BaseDto > ) Database . Fetch < MediaEntityDto > ( sql )
: Database . Fetch < BaseDto > ( sql ) ;
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
var entities = dtos . Select ( BuildEntity ) . ToArray ( ) ;
2018-04-20 16:07:34 +10:00
2018-04-24 17:59:26 +02:00
return entities ;
}
2022-02-24 09:24:56 +01:00
private IEnumerable < IEntitySlim > PerformGetAll ( Guid objectType , Action < Sql < ISqlContext > > ? filter = null )
2018-04-24 17:59:26 +02:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectType = = Cms . Core . Constants . ObjectTypes . Document | | objectType = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectType = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectType = = Cms . Core . Constants . ObjectTypes . Member ;
2018-04-24 17:59:26 +02:00
2019-10-14 14:34:33 +02:00
var sql = GetFullSqlForEntityType ( isContent , isMedia , isMember , objectType , filter ) ;
return GetEntities ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
2022-02-24 09:24:56 +01:00
public IEnumerable < TreeEntityPath > GetAllPaths ( Guid objectType , params int [ ] ? ids )
2017-12-07 16:45:25 +01:00
{
2022-02-24 09:24:56 +01:00
return ids ? . Any ( ) ? ? false
2017-12-07 16:45:25 +01:00
? PerformGetAllPaths ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . NodeId , ids . Distinct ( ) ) )
: PerformGetAllPaths ( objectType ) ;
}
2018-11-19 17:54:36 +11:00
public IEnumerable < TreeEntityPath > GetAllPaths ( Guid objectType , params Guid [ ] keys )
2017-12-07 16:45:25 +01:00
{
return keys . Any ( )
? PerformGetAllPaths ( objectType , sql = > sql . WhereIn < NodeDto > ( x = > x . UniqueId , keys . Distinct ( ) ) )
: PerformGetAllPaths ( objectType ) ;
}
2022-02-24 09:24:56 +01:00
private IEnumerable < TreeEntityPath > PerformGetAllPaths ( Guid objectType , Action < Sql < ISqlContext > > ? filter = null )
2017-12-07 16:45:25 +01:00
{
2019-02-08 08:50:57 +01:00
// NodeId is named Id on TreeEntityPath = use an alias
2019-02-08 12:42:46 +01:00
var sql = Sql ( ) . Select < NodeDto > ( x = > Alias ( x . NodeId , nameof ( TreeEntityPath . Id ) ) , x = > x . Path ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . NodeObjectType = = objectType ) ;
2017-12-07 16:45:25 +01:00
filter ? . Invoke ( sql ) ;
2018-01-10 12:48:51 +01:00
return Database . Fetch < TreeEntityPath > ( sql ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetByQuery ( IQuery < IUmbracoEntity > query )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sqlClause = GetBase ( false , false , false , null ) ;
2017-12-07 16:45:25 +01:00
var translator = new SqlTranslator < IUmbracoEntity > ( sqlClause , query ) ;
var sql = translator . Translate ( ) ;
2019-10-14 14:34:33 +02:00
sql = AddGroupBy ( false , false , false , sql , true ) ;
2017-12-07 16:45:25 +01:00
var dtos = Database . Fetch < BaseDto > ( sql ) ;
2019-11-05 15:05:51 +11:00
return dtos . Select ( BuildEntity ) . ToList ( ) ;
2017-12-07 16:45:25 +01:00
}
2018-11-19 17:54:36 +11:00
public IEnumerable < IEntitySlim > GetByQuery ( IQuery < IUmbracoEntity > query , Guid objectType )
2017-12-07 16:45:25 +01:00
{
2021-02-09 10:22:42 +01:00
var isContent = objectType = = Cms . Core . Constants . ObjectTypes . Document | | objectType = = Cms . Core . Constants . ObjectTypes . DocumentBlueprint ;
var isMedia = objectType = = Cms . Core . Constants . ObjectTypes . Media ;
var isMember = objectType = = Cms . Core . Constants . ObjectTypes . Member ;
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , null , new [ ] { objectType } ) ;
2018-04-20 13:12:55 +10:00
2017-12-07 16:45:25 +01:00
var translator = new SqlTranslator < IUmbracoEntity > ( sql , query ) ;
sql = translator . Translate ( ) ;
2019-10-14 14:34:33 +02:00
sql = AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
2019-10-14 14:34:33 +02:00
return GetEntities ( sql , isContent , isMedia , isMember ) ;
2017-12-07 16:45:25 +01:00
}
public UmbracoObjectTypes GetObjectType ( int id )
{
var sql = Sql ( ) . Select < NodeDto > ( x = > x . NodeObjectType ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . NodeId = = id ) ;
2018-01-15 11:32:30 +01:00
return ObjectTypes . GetUmbracoObjectType ( Database . ExecuteScalar < Guid > ( sql ) ) ;
2017-12-07 16:45:25 +01:00
}
public UmbracoObjectTypes GetObjectType ( Guid key )
{
var sql = Sql ( ) . Select < NodeDto > ( x = > x . NodeObjectType ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . UniqueId = = key ) ;
2018-01-15 11:32:30 +01:00
return ObjectTypes . GetUmbracoObjectType ( Database . ExecuteScalar < Guid > ( sql ) ) ;
2017-12-07 16:45:25 +01:00
}
2022-01-13 17:44:39 +00:00
public int ReserveId ( Guid key )
{
NodeDto node ;
Sql < ISqlContext > sql = SqlContext . Sql ( )
. Select < NodeDto > ( )
. From < NodeDto > ( )
. Where < NodeDto > ( x = > x . UniqueId = = key & & x . NodeObjectType = = Cms . Core . Constants . ObjectTypes . IdReservation ) ;
node = Database . SingleOrDefault < NodeDto > ( sql ) ;
if ( node ! = null )
throw new InvalidOperationException ( "An identifier has already been reserved for this Udi." ) ;
node = new NodeDto
{
UniqueId = key ,
Text = "RESERVED.ID" ,
NodeObjectType = Cms . Core . Constants . ObjectTypes . IdReservation ,
CreateDate = DateTime . Now ,
UserId = null ,
ParentId = - 1 ,
Level = 1 ,
Path = "-1" ,
SortOrder = 0 ,
Trashed = false
} ;
Database . Insert ( node ) ;
return node . NodeId ;
}
2017-12-07 16:45:25 +01:00
public bool Exists ( Guid key )
{
var sql = Sql ( ) . SelectCount ( ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . UniqueId = = key ) ;
return Database . ExecuteScalar < int > ( sql ) > 0 ;
}
public bool Exists ( int id )
{
var sql = Sql ( ) . SelectCount ( ) . From < NodeDto > ( ) . Where < NodeDto > ( x = > x . NodeId = = id ) ;
return Database . ExecuteScalar < int > ( sql ) > 0 ;
}
2018-09-25 10:55:33 +02:00
private DocumentEntitySlim BuildVariants ( DocumentEntitySlim entity )
= > BuildVariants ( new [ ] { entity } ) . First ( ) ;
2018-09-21 15:49:37 +10:00
2018-09-25 10:55:33 +02:00
private IEnumerable < DocumentEntitySlim > BuildVariants ( IEnumerable < DocumentEntitySlim > entities )
2018-09-21 15:49:37 +10:00
{
2022-02-24 09:24:56 +01:00
List < DocumentEntitySlim > ? v = null ;
2018-09-25 10:55:33 +02:00
var entitiesList = entities . ToList ( ) ;
foreach ( var e in entitiesList )
2018-09-21 15:49:37 +10:00
{
2018-09-25 10:55:33 +02:00
if ( e . Variations . VariesByCulture ( ) )
( v ? ? ( v = new List < DocumentEntitySlim > ( ) ) ) . Add ( e ) ;
}
2018-09-21 15:49:37 +10:00
2018-09-25 10:55:33 +02:00
if ( v = = null ) return entitiesList ;
2018-09-21 15:49:37 +10:00
2018-09-25 10:55:33 +02:00
// fetch all variant info dtos
2021-10-13 13:42:18 +02:00
var dtos = Database . FetchByGroups < VariantInfoDto , int > ( v . Select ( x = > x . Id ) , Constants . Sql . MaxParameterCount , GetVariantInfos ) ;
2018-09-25 10:55:33 +02:00
// group by node id (each group contains all languages)
var xdtos = dtos . GroupBy ( x = > x . NodeId ) . ToDictionary ( x = > x . Key , x = > x ) ;
foreach ( var e in v )
{
// since we're only iterating on entities that vary, we must have something
var edtos = xdtos [ e . Id ] ;
e . CultureNames = edtos . Where ( x = > x . CultureAvailable ) . ToDictionary ( x = > x . IsoCode , x = > x . Name ) ;
e . PublishedCultures = edtos . Where ( x = > x . CulturePublished ) . Select ( x = > x . IsoCode ) ;
e . EditedCultures = edtos . Where ( x = > x . CultureAvailable & & x . CultureEdited ) . Select ( x = > x . IsoCode ) ;
2018-09-21 15:49:37 +10:00
}
2018-09-25 10:55:33 +02:00
return entitiesList ;
2018-09-21 15:49:37 +10:00
}
2017-12-07 16:45:25 +01:00
#endregion
#region Sql
2018-09-25 10:55:33 +02:00
protected Sql < ISqlContext > GetVariantInfos ( IEnumerable < int > ids )
2018-09-21 15:49:37 +10:00
{
2018-09-25 10:55:33 +02:00
return Sql ( )
. Select < NodeDto > ( x = > x . NodeId )
2018-09-21 15:49:37 +10:00
. AndSelect < LanguageDto > ( x = > x . IsoCode )
2018-09-25 10:55:33 +02:00
. AndSelect < DocumentDto > ( "doc" , x = > Alias ( x . Published , "DocumentPublished" ) , x = > Alias ( x . Edited , "DocumentEdited" ) )
2018-09-25 16:55:00 +02:00
. AndSelect < DocumentCultureVariationDto > ( "dcv" ,
x = > Alias ( x . Available , "CultureAvailable" ) , x = > Alias ( x . Published , "CulturePublished" ) , x = > Alias ( x . Edited , "CultureEdited" ) ,
x = > Alias ( x . Name , "Name" ) )
2018-09-25 10:55:33 +02:00
// from node x language
2018-09-21 15:49:37 +10:00
. From < NodeDto > ( )
2018-09-25 10:55:33 +02:00
. CrossJoin < LanguageDto > ( )
// join to document - always exists - indicates global document published/edited status
. InnerJoin < DocumentDto > ( "doc" )
. On < NodeDto , DocumentDto > ( ( node , doc ) = > node . NodeId = = doc . NodeId , aliasRight : "doc" )
// left-join do document variation - matches cultures that are *available* + indicates when *edited*
. LeftJoin < DocumentCultureVariationDto > ( "dcv" )
. On < NodeDto , DocumentCultureVariationDto , LanguageDto > ( ( node , dcv , lang ) = > node . NodeId = = dcv . NodeId & & lang . Id = = dcv . LanguageId , aliasRight : "dcv" )
// for selected nodes
v10 SQLite support + distributed locking abstractions (#11922)
* Created Persistence.SQLite project skeleton.
* SQLite database initialization
* Various changes and hacks to make things work.
* WIP integration tests
* Fix thread safety tests
* Fix tests that relied on tie breaker sorting.
Spent a fair amount of time looking for a less lazy fix but gave up.
* Convert right join to left join ContentTypeRepository.PerformGetByQuery
SQLite doesn't support right join
* Fix test Can_Generate_Delete_SubQuery_Statement
Worth noting that NPoco.DatabaseTypes.SQLiteDatabaseType doesn't override
EscapeSqlIdentifier so NPoco will escape with [].
SQLite docs say > "A keyword enclosed in square brackets is an identifier.
This is not standard SQL.
This quoting mechanism is used by MS Access and SQL Server and is
included in SQLite for compatibility."
Also could have updated SqliteSyntaxProvider to match npoco but
decided against it.
* Fixes for paginated custom order by
* Fix tests broken by lack of unique indexes.
* Fix SqlServerTableByTableTest tests.
These tests didn't actually do anything as the tables already exist so schema creator just returned.
Did however point out that the default implementation for DoesTableExist just returns false so added a default naive implementation.
* Fix ValidateLoginSession - SelectTop must come later
* dry up database cleanup
* Fix up db migration tests.
We can't drop pk in sqlite without recreating table.
Test looks to be testing that add column works as intended which we can test.
* Prevent schema creation errors.
* SQLite ignore lock tests, WAL back on.
* Fix package schema tests
* Fix NPocoFetchTests - case sensitivity not under test
* Fix AdvancedMigrationTests (where possible)
Migrations probably need a good look later.
Maybe nuke old migrations and only support moving to v10 from v9.
If we do that can do some cleanup.
* Cleanup test database configuration
* Run integration tests against SQLite on build agent.
* Drop MS.Data.SQLite
System.Data.SQLite was quicker to roll out due to more CLR type mapping
* YAML
* Skip Umbraco.Tests.Integration.SqlCe
* Drop SqlServerTableByTable tests.
Until this week they did nothing anyway as they with NewSchemaPerTest
so the tests all passed as CreateTable was no op (already exists).
Also all of the tables are created in an empty database by SchemaValidationTest.cs
DatabaseSchemaCreation_Produces_DatabaseSchemaResult_With_Zero_Errors
* Might aswell run against macOS also.
* Copy azure pipelines task header layout
* Delete SQLCe projects
* Remove SQL CE specific code.
* Remove SQL CE NuSpec, template params, build script setup
* Delete umbraco-netcore-only.sln
* Add SkipTests solution configuration and use for codeql
* Remove reference to deleted nuspec file.
* Refactor ConnectionStrings WRT DataDirectory placeholder & ProviderName.
At this point you can try out SQLite support by setting the following
in appsettings.json and then completing the install process.
"ConnectionStrings": {
"umbracoDbDSN": "Data Source=|DataDirectory|/umbraco.sqlite",
"umbracoDbDSN_ProviderName": "System.Data.SQLite"
},
Not currently possible via installer UI without provider name pre-set in
configuration.
* Switch to Microsoft.Data.Sqlite
Some gross hacks but will be good to find out if this works
with apple silicon.
* Enable selection of SQLite via installer UI (also quick install)
* Remove SqlServerDbProviderFactoryCreator to cleanup a TODO
* Move SQL Server support to its own class library
* Add persistence dependencies to Umbraco.CMS metapackage
* Bugfix packages delete query
Created invalid query for SQLite.
* Try out cypress tests Linux + SQLite
* Prevent cypress test artifact upload failure on attempt 2+
* LocalDb bugfixes
* Drop redundant enum
* Move SqlClient constant
* Misc whitespace
* Remove IsSqlCe extension (TODO: drop non 9->10 migrations later).
* Umbraco.Persistence.* -> Umbraco.Cms.Persistence.*
* Display quick install defaults and per provider default database name.
* Misc remove old comment
* little re-arrange
* Remove almost all usages of IsSqlite extension.
* visual adjustments
* Custom Database Configuration is last step and should then say Install.
* use text instead of disabled inputs
* move legend, rename to Install
* Update SqlMainDomLock to work without distributed locks.
* Added IDistributedLockingMechanism interface and in memory impl.
* Drop locking from ISqlSyntaxProvider & wire up scope to abstraction.
* Added SqlServerDistributedLockingMechanism
* Move distributed locking interfaces and exceptions to Core + xmldocs.
* Fix tests, Misc cleanup, Add SQL distributed locking integration tests
* Provide mechanism to specify DistributedLockingMechanism in config
(even if added by composer)
* Nomplementation -> NoImplementation
* Fix misleading comment
* Integration tests use SqlServerDistributedLockingMechanism when possible
* Handle up-gradable locks SqlServerDistributedLockingMechanism.
TODO: InMemoryDistributedLockingMechanism.
Note: Nuked SqlServerDistributedLockingMechanismTests, will still sleep
at night.
Is covered by Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.LockTests
* Make tests pass for InMemoryDistributedLockingMechanism, pretty hacky.
* Tweak constraints on WithCollectionBuilder so i can drop bad constructor
* Added SqliteDistributedLockingMechanism
* Dropped InMemoryDistributedMechanism + magic
InMemoryDistributedMechanism was pretty rubbish and now we have
a decent implementation for SQLite as we no longer block readers
see 8d1f42b.
Also drop the CollectionBuilder setup, instead do the same as we do
for syntax providers etc, it's more automagical so we never require an
explicit selection although we are allowing for it.
However keeping the optional IUmbracoBuilder constructor param for
CollectionBuilders as it's extremely useful.
* Fix quick install "" database name.
* Hide Database Configuration section when a connection string is pre-set.
Doesn't seem worth it to extract db name from connection string.
* Ensure wal test 2+
* Fix logging inconsistencies.
* Ensure in transaction when obtaining locks + no-op the SQLite read lock.
There's no point in running the query just to make a single test pass.
* Fix installer database display names
* Allow SQLite shared cache without losing deferred transactions
* Opt into shared cache for new SQLite databases + fix filename
* Fix misc inconsistency in .gitignore
* Prefer our interceptor interface
* Restore DEBUG_DATABASES code OnConnectionOpened in case it's used.
* Back to private cache.
* Added retry strategy for SQLite + refactor out SQL server specific stuff
* Fix SQL server tests.
* Misc - Orphaned comment, incorrect casing.
* InMemory SQLite test database & turn shared cache back on everywhere.
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2022-03-11 16:14:20 +00:00
. WhereIn < NodeDto > ( x = > x . NodeId , ids )
. OrderBy < LanguageDto > ( x = > x . Id ) ;
2018-09-21 15:49:37 +10:00
}
2018-09-25 10:55:33 +02:00
2017-12-07 16:45:25 +01:00
// gets the full sql for a given object type and a given unique id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetFullSqlForEntityType ( bool isContent , bool isMedia , bool isMember , Guid objectType , Guid uniqueId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , objectType , uniqueId ) ;
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the full sql for a given object type and a given node id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetFullSqlForEntityType ( bool isContent , bool isMedia , bool isMember , Guid objectType , int nodeId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , objectType , nodeId ) ;
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the full sql for a given object type, with a given filter
2022-02-24 09:24:56 +01:00
protected Sql < ISqlContext > GetFullSqlForEntityType ( bool isContent , bool isMedia , bool isMember , Guid objectType , Action < Sql < ISqlContext > > ? filter )
2017-12-07 16:45:25 +01:00
{
2019-11-05 15:05:51 +11:00
var sql = GetBaseWhere ( isContent , isMedia , isMember , false , filter , new [ ] { objectType } ) ;
2019-10-14 14:34:33 +02:00
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM [+ filter] sql
// always from the 'current' content version
2022-02-24 09:24:56 +01:00
protected Sql < ISqlContext > GetBase ( bool isContent , bool isMedia , bool isMember , Action < Sql < ISqlContext > > ? filter , bool isCount = false )
2019-11-05 15:05:51 +11:00
{
2017-12-07 16:45:25 +01:00
var sql = Sql ( ) ;
if ( isCount )
{
sql . SelectCount ( ) ;
}
else
{
sql
. Select < NodeDto > ( x = > x . NodeId , x = > x . Trashed , x = > x . ParentId , x = > x . UserId , x = > x . Level , x = > x . Path )
. AndSelect < NodeDto > ( x = > x . SortOrder , x = > x . UniqueId , x = > x . Text , x = > x . NodeObjectType , x = > x . CreateDate )
. Append ( ", COUNT(child.id) AS children" ) ;
2019-10-14 14:34:33 +02:00
if ( isContent | | isMedia | | isMember )
2017-12-07 16:45:25 +01:00
sql
2020-08-26 08:05:15 +02:00
. AndSelect < ContentVersionDto > ( x = > Alias ( x . Id , "versionId" ) , x = > x . VersionDate )
2018-05-07 18:22:23 +02:00
. AndSelect < ContentTypeDto > ( x = > x . Alias , x = > x . Icon , x = > x . Thumbnail , x = > x . IsContainer , x = > x . Variations ) ;
2018-04-20 13:12:55 +10:00
if ( isContent )
{
sql
2018-09-25 10:55:33 +02:00
. AndSelect < DocumentDto > ( x = > x . Published , x = > x . Edited ) ;
2018-04-20 13:12:55 +10:00
}
2019-06-13 23:39:36 +10:00
if ( isMedia )
{
sql
. AndSelect < MediaVersionDto > ( x = > Alias ( x . Path , "MediaPath" ) ) ;
}
2017-12-07 16:45:25 +01:00
}
sql
. From < NodeDto > ( ) ;
2019-10-14 14:34:33 +02:00
if ( isContent | | isMedia | | isMember )
2017-12-07 16:45:25 +01:00
{
sql
2019-11-05 15:05:51 +11:00
. LeftJoin < ContentVersionDto > ( ) . On < NodeDto , ContentVersionDto > ( ( left , right ) = > left . NodeId = = right . NodeId & & right . Current )
. LeftJoin < ContentDto > ( ) . On < NodeDto , ContentDto > ( ( left , right ) = > left . NodeId = = right . NodeId )
. LeftJoin < ContentTypeDto > ( ) . On < ContentDto , ContentTypeDto > ( ( left , right ) = > left . ContentTypeId = = right . NodeId ) ;
2017-12-07 16:45:25 +01:00
}
if ( isContent )
{
sql
2019-11-05 15:05:51 +11:00
. LeftJoin < DocumentDto > ( ) . On < NodeDto , DocumentDto > ( ( left , right ) = > left . NodeId = = right . NodeId ) ;
2017-12-07 16:45:25 +01:00
}
2019-06-13 23:39:36 +10:00
if ( isMedia )
{
sql
2019-08-24 17:29:37 +01:00
. LeftJoin < MediaVersionDto > ( ) . On < ContentVersionDto , MediaVersionDto > ( ( left , right ) = > left . Id = = right . Id ) ;
2019-06-13 23:39:36 +10:00
}
2018-04-20 13:12:55 +10:00
//Any LeftJoin statements need to come last
2017-12-07 16:45:25 +01:00
if ( isCount = = false )
2018-04-20 13:12:55 +10:00
{
2017-12-07 16:45:25 +01:00
sql
. LeftJoin < NodeDto > ( "child" ) . On < NodeDto , NodeDto > ( ( left , right ) = > left . NodeId = = right . ParentId , aliasRight : "child" ) ;
2018-04-20 13:12:55 +10:00
}
2017-12-07 16:45:25 +01:00
filter ? . Invoke ( sql ) ;
return sql ;
}
// gets the base SELECT + FROM [+ filter] + WHERE sql
// for a given object type, with a given filter
2022-02-24 09:24:56 +01:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Action < Sql < ISqlContext > > ? filter , Guid [ ] objectTypes )
2017-12-07 16:45:25 +01:00
{
2019-11-06 12:43:10 +11:00
var sql = GetBase ( isContent , isMedia , isMember , filter , isCount ) ;
if ( objectTypes . Length > 0 )
{
sql . WhereIn < NodeDto > ( x = > x . NodeObjectType , objectTypes ) ;
}
return sql ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM + WHERE sql
// for a given node id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , int id )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . NodeId = = id ) ;
2019-10-14 14:34:33 +02:00
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM + WHERE sql
// for a given unique id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Guid uniqueId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
var sql = GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . UniqueId = = uniqueId ) ;
2019-10-14 14:34:33 +02:00
return AddGroupBy ( isContent , isMedia , isMember , sql , true ) ;
2017-12-07 16:45:25 +01:00
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and node id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Guid objectType , int nodeId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
return GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . NodeId = = nodeId & & x . NodeObjectType = = objectType ) ;
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and unique id
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > GetBaseWhere ( bool isContent , bool isMedia , bool isMember , bool isCount , Guid objectType , Guid uniqueId )
2017-12-07 16:45:25 +01:00
{
2019-10-14 14:34:33 +02:00
return GetBase ( isContent , isMedia , isMember , null , isCount )
2017-12-07 16:45:25 +01:00
. Where < NodeDto > ( x = > x . UniqueId = = uniqueId & & x . NodeObjectType = = objectType ) ;
}
// gets the GROUP BY / ORDER BY sql
// required in order to count children
2019-10-14 14:34:33 +02:00
protected Sql < ISqlContext > AddGroupBy ( bool isContent , bool isMedia , bool isMember , Sql < ISqlContext > sql , bool defaultSort )
2017-12-07 16:45:25 +01:00
{
sql
. GroupBy < NodeDto > ( x = > x . NodeId , x = > x . Trashed , x = > x . ParentId , x = > x . UserId , x = > x . Level , x = > x . Path )
. AndBy < NodeDto > ( x = > x . SortOrder , x = > x . UniqueId , x = > x . Text , x = > x . NodeObjectType , x = > x . CreateDate ) ;
if ( isContent )
2018-04-20 13:12:55 +10:00
{
2017-12-07 16:45:25 +01:00
sql
2018-09-25 10:55:33 +02:00
. AndBy < DocumentDto > ( x = > x . Published , x = > x . Edited ) ;
2018-04-20 13:12:55 +10:00
}
2019-06-13 23:39:36 +10:00
if ( isMedia )
{
sql
. AndBy < MediaVersionDto > ( x = > Alias ( x . Path , "MediaPath" ) ) ;
}
2017-12-07 16:45:25 +01:00
2019-10-14 14:34:33 +02:00
if ( isContent | | isMedia | | isMember )
2017-12-07 16:45:25 +01:00
sql
2020-08-26 08:05:15 +02:00
. AndBy < ContentVersionDto > ( x = > x . Id , x = > x . VersionDate )
2018-05-07 18:22:23 +02:00
. AndBy < ContentTypeDto > ( x = > x . Alias , x = > x . Icon , x = > x . Thumbnail , x = > x . IsContainer , x = > x . Variations ) ;
2017-12-07 16:45:25 +01:00
2018-12-21 13:15:46 +11:00
if ( defaultSort )
2017-12-07 16:45:25 +01:00
sql . OrderBy < NodeDto > ( x = > x . SortOrder ) ;
return sql ;
}
2018-12-21 13:15:46 +11:00
private void ApplyOrdering ( ref Sql < ISqlContext > sql , Ordering ordering )
{
if ( sql = = null ) throw new ArgumentNullException ( nameof ( sql ) ) ;
if ( ordering = = null ) throw new ArgumentNullException ( nameof ( ordering ) ) ;
2019-11-05 15:05:51 +11:00
// TODO: although the default ordering string works for name, it wont work for others without a table or an alias of some sort
// As more things are attempted to be sorted we'll prob have to add more expressions here
2019-11-11 12:05:26 +00:00
string orderBy ;
2022-02-24 09:24:56 +01:00
switch ( ordering . OrderBy ? . ToUpperInvariant ( ) )
2019-11-05 15:05:51 +11:00
{
2019-11-11 12:05:26 +00:00
case "PATH" :
orderBy = SqlSyntax . GetQuotedColumn ( NodeDto . TableName , "path" ) ;
break ;
default :
2022-02-24 09:24:56 +01:00
orderBy = ordering . OrderBy ? ? string . Empty ;
2019-11-11 12:05:26 +00:00
break ;
2021-02-09 10:22:42 +01:00
}
2018-12-21 13:15:46 +11:00
if ( ordering . Direction = = Direction . Ascending )
sql . OrderBy ( orderBy ) ;
else
sql . OrderByDescending ( orderBy ) ;
}
2017-12-07 16:45:25 +01:00
#endregion
#region Classes
/// <summary>
2019-11-05 15:05:51 +11:00
/// The DTO used to fetch results for a generic content item which could be either a document, media or a member
2017-12-07 16:45:25 +01:00
/// </summary>
2019-11-05 15:05:51 +11:00
private class GenericContentEntityDto : DocumentEntityDto
{
2022-02-24 09:24:56 +01:00
public string? MediaPath { get ; set ; }
2019-11-05 15:05:51 +11:00
}
/// <summary>
/// The DTO used to fetch results for a document item with its variation info
2017-12-07 16:45:25 +01:00
/// </summary>
2019-11-05 15:05:51 +11:00
private class DocumentEntityDto : BaseDto
2018-09-21 15:49:37 +10:00
{
public ContentVariation Variations { get ; set ; }
public bool Published { get ; set ; }
public bool Edited { get ; set ; }
}
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
/// <summary>
/// The DTO used to fetch results for a media item with its media path info
/// </summary>
2019-06-13 23:39:36 +10:00
private class MediaEntityDto : BaseDto
{
2022-02-24 09:24:56 +01:00
public string? MediaPath { get ; set ; }
2019-06-13 23:39:36 +10:00
}
2019-11-05 15:05:51 +11:00
/// <summary>
/// The DTO used to fetch results for a member item
/// </summary>
2019-10-14 14:34:33 +02:00
private class MemberEntityDto : BaseDto
{
}
2018-09-25 10:55:33 +02:00
public class VariantInfoDto
2018-09-21 15:49:37 +10:00
{
public int NodeId { get ; set ; }
2022-02-24 09:24:56 +01:00
public string IsoCode { get ; set ; } = null ! ;
public string Name { get ; set ; } = null ! ;
2018-09-25 10:55:33 +02:00
public bool DocumentPublished { get ; set ; }
public bool DocumentEdited { get ; set ; }
2018-09-25 16:55:00 +02:00
public bool CultureAvailable { get ; set ; }
public bool CulturePublished { get ; set ; }
public bool CultureEdited { get ; set ; }
2018-09-21 15:49:37 +10:00
}
// ReSharper disable once ClassNeverInstantiated.Local
/// <summary>
/// the DTO corresponding to fields selected by GetBase
/// </summary>
private class BaseDto
{
// ReSharper disable UnusedAutoPropertyAccessor.Local
// ReSharper disable UnusedMember.Local
public int NodeId { get ; set ; }
public bool Trashed { get ; set ; }
public int ParentId { get ; set ; }
public int? UserId { get ; set ; }
public int Level { get ; set ; }
2022-02-24 09:24:56 +01:00
public string Path { get ; set ; } = null ! ;
2018-09-21 15:49:37 +10:00
public int SortOrder { get ; set ; }
public Guid UniqueId { get ; set ; }
2022-02-24 09:24:56 +01:00
public string? Text { get ; set ; }
2018-09-21 15:49:37 +10:00
public Guid NodeObjectType { get ; set ; }
public DateTime CreateDate { get ; set ; }
2020-08-26 08:05:15 +02:00
public DateTime VersionDate { get ; set ; }
2018-09-21 15:49:37 +10:00
public int Children { get ; set ; }
public int VersionId { get ; set ; }
2022-02-24 09:24:56 +01:00
public string Alias { get ; set ; } = null ! ;
public string? Icon { get ; set ; }
public string? Thumbnail { get ; set ; }
2018-09-21 15:49:37 +10:00
public bool IsContainer { get ; set ; }
// ReSharper restore UnusedAutoPropertyAccessor.Local
// ReSharper restore UnusedMember.Local
}
2017-12-07 16:45:25 +01:00
#endregion
#region Factory
2019-11-05 15:05:51 +11:00
private EntitySlim BuildEntity ( BaseDto dto )
2018-01-12 10:48:36 +01:00
{
2021-02-09 10:22:42 +01:00
if ( dto . NodeObjectType = = Cms . Core . Constants . ObjectTypes . Document )
2018-01-12 10:48:36 +01:00
return BuildDocumentEntity ( dto ) ;
2021-02-09 10:22:42 +01:00
if ( dto . NodeObjectType = = Cms . Core . Constants . ObjectTypes . Media )
2019-06-13 23:39:36 +10:00
return BuildMediaEntity ( dto ) ;
2021-02-09 10:22:42 +01:00
if ( dto . NodeObjectType = = Cms . Core . Constants . ObjectTypes . Member )
2019-10-14 14:34:33 +02:00
return BuildMemberEntity ( dto ) ;
2017-12-07 16:45:25 +01:00
2018-01-15 11:32:30 +01:00
// EntitySlim does not track changes
var entity = new EntitySlim ( ) ;
BuildEntity ( entity , dto ) ;
2018-01-12 10:48:36 +01:00
return entity ;
}
2017-12-07 16:45:25 +01:00
2018-01-15 11:32:30 +01:00
private static void BuildEntity ( EntitySlim entity , BaseDto dto )
2017-12-07 16:45:25 +01:00
{
2018-01-12 10:48:36 +01:00
entity . Trashed = dto . Trashed ;
entity . CreateDate = dto . CreateDate ;
2020-08-26 08:05:15 +02:00
entity . UpdateDate = dto . VersionDate ;
2021-02-09 10:22:42 +01:00
entity . CreatorId = dto . UserId ? ? Cms . Core . Constants . Security . UnknownUserId ;
2018-01-12 10:48:36 +01:00
entity . Id = dto . NodeId ;
entity . Key = dto . UniqueId ;
entity . Level = dto . Level ;
entity . Name = dto . Text ;
entity . NodeObjectType = dto . NodeObjectType ;
entity . ParentId = dto . ParentId ;
entity . Path = dto . Path ;
entity . SortOrder = dto . SortOrder ;
entity . HasChildren = dto . Children > 0 ;
2018-01-15 11:32:30 +01:00
entity . IsContainer = dto . IsContainer ;
2018-01-12 10:48:36 +01:00
}
2018-01-15 11:32:30 +01:00
private static void BuildContentEntity ( ContentEntitySlim entity , BaseDto dto )
2018-01-12 10:48:36 +01:00
{
BuildEntity ( entity , dto ) ;
entity . ContentTypeAlias = dto . Alias ;
entity . ContentTypeIcon = dto . Icon ;
entity . ContentTypeThumbnail = dto . Thumbnail ;
}
2019-06-13 23:39:36 +10:00
private MediaEntitySlim BuildMediaEntity ( BaseDto dto )
2018-01-12 10:48:36 +01:00
{
2018-01-15 11:32:30 +01:00
// EntitySlim does not track changes
2019-06-13 23:39:36 +10:00
var entity = new MediaEntitySlim ( ) ;
2018-01-15 11:32:30 +01:00
BuildContentEntity ( entity , dto ) ;
2019-06-13 23:39:36 +10:00
2020-03-01 20:07:49 +01:00
// fill in the media info
if ( dto is MediaEntityDto mediaEntityDto )
2019-06-13 23:39:36 +10:00
{
2020-03-01 20:07:49 +01:00
entity . MediaPath = mediaEntityDto . MediaPath ;
}
else if ( dto is GenericContentEntityDto genericContentEntityDto )
{
entity . MediaPath = genericContentEntityDto . MediaPath ;
2019-06-13 23:39:36 +10:00
}
2018-01-12 10:48:36 +01:00
return entity ;
}
2017-12-07 16:45:25 +01:00
2018-05-07 23:22:52 +10:00
private DocumentEntitySlim BuildDocumentEntity ( BaseDto dto )
2018-01-12 10:48:36 +01:00
{
2018-01-15 11:32:30 +01:00
// EntitySlim does not track changes
var entity = new DocumentEntitySlim ( ) ;
2018-05-08 12:43:07 +10:00
BuildContentEntity ( entity , dto ) ;
2017-12-07 16:45:25 +01:00
2019-11-05 15:05:51 +11:00
if ( dto is DocumentEntityDto contentDto )
2018-04-20 13:12:55 +10:00
{
2018-09-25 10:55:33 +02:00
// fill in the invariant info
entity . Edited = contentDto . Edited ;
entity . Published = contentDto . Published ;
entity . Variations = contentDto . Variations ;
2018-04-20 13:12:55 +10:00
}
2018-09-25 10:55:33 +02:00
2018-04-20 13:12:55 +10:00
return entity ;
}
2019-10-14 14:34:33 +02:00
private MemberEntitySlim BuildMemberEntity ( BaseDto dto )
{
// EntitySlim does not track changes
var entity = new MemberEntitySlim ( ) ;
BuildEntity ( entity , dto ) ;
entity . ContentTypeAlias = dto . Alias ;
entity . ContentTypeIcon = dto . Icon ;
entity . ContentTypeThumbnail = dto . Thumbnail ;
return entity ;
}
2017-12-07 16:45:25 +01:00
#endregion
}
}