From 07bb57fac8d5721255b4585e7e196a55c4cc3e78 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 25 Oct 2016 12:22:06 +0200 Subject: [PATCH 01/11] U4-9018 - provide a way to disable server master election --- .../Sync/SingleServerRegistrar.cs | 37 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 2 files changed, 38 insertions(+) create mode 100644 src/Umbraco.Core/Sync/SingleServerRegistrar.cs diff --git a/src/Umbraco.Core/Sync/SingleServerRegistrar.cs b/src/Umbraco.Core/Sync/SingleServerRegistrar.cs new file mode 100644 index 0000000000..117ce516be --- /dev/null +++ b/src/Umbraco.Core/Sync/SingleServerRegistrar.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Sync +{ + public class SingleServerRegistrar : IServerRegistrar2 + { + private readonly string _umbracoApplicationUrl; + + public IEnumerable Registrations { get; private set; } + + public SingleServerRegistrar() + { + _umbracoApplicationUrl = ApplicationContext.Current.UmbracoApplicationUrl; + Registrations = new[] { new ServerAddressImpl(_umbracoApplicationUrl) }; + } + + public ServerRole GetCurrentServerRole() + { + return ServerRole.Single; + } + + public string GetCurrentServerUmbracoApplicationUrl() + { + return _umbracoApplicationUrl; + } + + private class ServerAddressImpl : IServerAddress + { + public ServerAddressImpl(string serverAddress) + { + ServerAddress = serverAddress; + } + + public string ServerAddress { get; private set; } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index bf70f6afd6..e8ef0c64f2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1330,6 +1330,7 @@ + From b617b9e39101b237cf425973fd6e1a99242213e0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 25 Oct 2016 12:32:50 +0200 Subject: [PATCH 02/11] U4-9108 - add config setting --- src/Umbraco.Core/CoreBootManager.cs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 4da740f458..4ef08bd02f 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; using System.Threading; @@ -30,12 +31,13 @@ using Umbraco.Core.Manifest; using Umbraco.Core.Services; using Umbraco.Core.Sync; using Umbraco.Core.Strings; +using IntegerValidator = Umbraco.Core.PropertyEditors.IntegerValidator; using MigrationsVersionFourNineZero = Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero; namespace Umbraco.Core { /// - /// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application + /// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application /// /// /// This does not provide any startup functionality relating to web objects @@ -191,14 +193,14 @@ namespace Umbraco.Core protected virtual CacheHelper CreateApplicationCache() { var cacheHelper = new CacheHelper( - //we need to have the dep clone runtime cache provider to ensure + //we need to have the dep clone runtime cache provider to ensure //all entities are cached properly (cloned in and cloned out) new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()), new StaticCacheProvider(), //we have no request based cache when not running in web-based context new NullCacheProvider(), new IsolatedRuntimeCache(type => - //we need to have the dep clone runtime cache provider to ensure + //we need to have the dep clone runtime cache provider to ensure //all entities are cached properly (cloned in and cloned out) new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); @@ -251,18 +253,18 @@ namespace Umbraco.Core } /// - /// Special method to initialize the ApplicationEventsResolver and any modifications required for it such + /// Special method to initialize the ApplicationEventsResolver and any modifications required for it such /// as adding custom types to the resolver. /// protected virtual void InitializeApplicationEventsResolver() { //find and initialize the application startup handlers, we need to initialize this resolver here because - //it is a special resolver where they need to be instantiated first before any other resolvers in order to bind to + //it is a special resolver where they need to be instantiated first before any other resolvers in order to bind to //events and to call their events during bootup. //ApplicationStartupHandler.RegisterHandlers(); //... and set the special flag to let us resolve before frozen resolution ApplicationEventsResolver.Current = new ApplicationEventsResolver( - ServiceProvider, + ServiceProvider, ProfilingLogger.Logger, PluginManager.ResolveApplicationStartupHandlers()) { @@ -282,7 +284,7 @@ namespace Umbraco.Core } /// - /// Fires after initialization and calls the callback to allow for customizations to occur & + /// Fires after initialization and calls the callback to allow for customizations to occur & /// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called /// /// @@ -334,7 +336,7 @@ namespace Umbraco.Core { if (_isComplete) throw new InvalidOperationException("The boot manager has already been completed"); - + FreezeResolution(); //Here we need to make sure the db can be connected to @@ -366,7 +368,7 @@ namespace Umbraco.Core ProfilingLogger.Logger.Error("An error occurred running OnApplicationStarted for handler " + x.GetType(), ex); throw; } - }); + }); } //Now, startup all of our legacy startup handler @@ -455,6 +457,10 @@ namespace Umbraco.Core { ServerRegistrarResolver.Current = new ServerRegistrarResolver(new ConfigServerRegistrar()); } + else if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) + { + ServerRegistrarResolver.Current = new ServerRegistrarResolver(new SingleServerRegistrar()); + } else { ServerRegistrarResolver.Current = new ServerRegistrarResolver( @@ -462,7 +468,6 @@ namespace Umbraco.Core new Lazy(() => ApplicationContext.Services.ServerRegistrationService), new DatabaseServerRegistrarOptions())); } - //by default we'll use the database server messenger with default options (no callbacks), // this will be overridden in the web startup @@ -473,7 +478,7 @@ namespace Umbraco.Core ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveAssignedMapperTypes()); - + //RepositoryResolver.Current = new RepositoryResolver( // new RepositoryFactory(ApplicationCache)); From 4f211aaf48daa789aac2d692e4550f4aa5a0b7d0 Mon Sep 17 00:00:00 2001 From: ricardo abreu Date: Thu, 3 Nov 2016 17:13:27 +0000 Subject: [PATCH 03/11] added finish button so page only reloads after user input. also added localization for en/en_us --- .../packager/views/install-local.controller.js | 16 ++++++++++++---- .../src/views/packager/views/install-local.html | 13 +++++++++++++ .../src/views/packager/views/repo.controller.js | 14 ++++++++++---- .../src/views/packager/views/repo.html | 10 ++++++++++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 1 + 6 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js index e5fba69a88..e34bc48ecd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js @@ -12,6 +12,7 @@ status: "", progress:0 }; + vm.installCompleted = false; vm.zipFile = { uploadStatus: "idle", uploadProgress: 0, @@ -137,10 +138,10 @@ localStorageService.set("packageInstallUri", "installed"); } - //reload on next digest (after cookie) - $timeout(function () { - $window.location.reload(true); - }); + vm.installState.status = localizationService.localize("packager_installStateCompleted"); + vm.installCompleted = true; + + }, installError); @@ -150,6 +151,13 @@ //This will return a rejection meaning that the promise change above will stop return $q.reject(); } + + vm.reloadPage = function() { + //reload on next digest (after cookie) + $timeout(function () { + $window.location.reload(true); + }); + } } angular.module("umbraco").controller("Umbraco.Editors.Packages.InstallLocalController", PackagesInstallLocalController); diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index 499e844588..6bb7c6fb14 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -159,6 +159,19 @@

{{vm.installState.status}}

+ + +
+ + +
+ diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js index e4afb661e3..fd9e7aaa02 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js @@ -30,6 +30,7 @@ vm.openLightbox = openLightbox; vm.closeLightbox = closeLightbox; vm.search = search; + vm.installCompleted = false; var currSort = "Latest"; //used to cancel any request in progress if another one needs to take it's place @@ -215,10 +216,8 @@ localStorageService.set("packageInstallUri", result.postInstallationPath); } - //reload on next digest (after cookie) - $timeout(function() { - window.location.reload(true); - }); + vm.installState.status = localizationService.localize("packager_installStateCompleted"); + vm.installCompleted = true; }, error); @@ -277,6 +276,13 @@ searchDebounced(); } + vm.reloadPage = function () { + //reload on next digest (after cookie) + $timeout(function () { + $window.location.reload(true); + }); + } + init(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index 3975dc96d9..e7b14182ca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -340,6 +340,16 @@

{{vm.installState.status}}

+
+ + +
+ diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index f710e54215..50511bd61c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -825,7 +825,7 @@ To manage your website, simply open the Umbraco back office and start adding con Installing... Restarting, please wait... All done, your browser will now refresh, please wait... - + Please click finish to complete installation and reload page. Paste with full formatting (Not recommended) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 80c6faf07b..bb4016dded 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -825,6 +825,7 @@ To manage your website, simply open the Umbraco back office and start adding con Installing... Restarting, please wait... All done, your browser will now refresh, please wait... + Please click finish to complete installation and reload page. Paste with full formatting (Not recommended) From 7085f198a90eb18d63bdef9de671950ce14c6daa Mon Sep 17 00:00:00 2001 From: ricardo abreu Date: Thu, 3 Nov 2016 17:29:46 +0000 Subject: [PATCH 04/11] changed reloadPage function ($window to window) --- .../src/views/packager/views/repo.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js index fd9e7aaa02..5ae1d4bf2c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js @@ -279,7 +279,7 @@ vm.reloadPage = function () { //reload on next digest (after cookie) $timeout(function () { - $window.location.reload(true); + window.location.reload(true); }); } From 42522f5af3476787c2c6f9bcf558dd502a2984f2 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 9 Nov 2016 16:45:06 +0100 Subject: [PATCH 05/11] U4-6994 - remove N+1 in ContentRepository.GetByPublishedVersion, .GetAllVersions --- .../Repositories/ContentRepository.cs | 84 ++++++++++++++----- .../Repositories/RelationRepository.cs | 2 - 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 84caeb2a3e..5d8818e1ac 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -254,6 +254,14 @@ namespace Umbraco.Core.Persistence.Repositories } while (processed < total); } + public override IEnumerable GetAllVersions(int id) + { + var sql = GetBaseQuery(false) + .Where(GetBaseWhereClause(), new { Id = id }) + .OrderByDescending(x => x.VersionDate, SqlSyntax); + return GetAllBySql(sql); + } + public override IContent GetByVersion(Guid versionId) { var sql = GetBaseQuery(false); @@ -662,28 +670,64 @@ namespace Umbraco.Core.Persistence.Repositories .OrderBy(x => x.Level, SqlSyntax) .OrderBy(x => x.SortOrder, SqlSyntax); - //NOTE: This doesn't allow properties to be part of the query - var dtos = Database.Fetch(sql); - - foreach (var dto in dtos) - { - //Check in the cache first. If it exists there AND it is published - // then we can use that entity. Otherwise if it is not published (which can be the case - // because we only store the 'latest' entries in the cache which might not be the published - // version) - var fromCache = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); - //var fromCache = TryGetFromCache(dto.NodeId); - if (fromCache != null && fromCache.Published) - { - yield return fromCache; - } - else - { - yield return CreateContentFromDto(dto, dto.VersionId, sql); - } - } + return GetAllBySql(sql); } + private IEnumerable GetAllBySql(Sql sql) + { + var dtos = Database.Fetch(sql); + var content = new IContent[dtos.Count]; + var defs = new List(); + + for (var i = 0; i < dtos.Count; i++) + { + var dto = dtos[i]; + + // if the cache contains the published version, use it + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null && cached.Published) + { + content[i] = cached; + continue; + } + + // else, need to fetch the version from the database + var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); + var c = factory.BuildEntity(dto); + if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) + c.Template = _templateRepository.Get(dto.TemplateId.Value); + content[i] = c; + + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.VersionId, + dto.ContentVersionDto.VersionDate, + dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, + contentType + )); + } + + // going to load all properties for all docs from database, + // but at least in one queries thus avoiding N+1 + var propertyData = GetPropertyCollection(sql, defs); + + var dtoIndex = 0; + var defIndex = 0; + while (true) + { + if (defIndex == defs.Count) return content; + while (dtoIndex < dtos.Count && dtos[dtoIndex].NodeId != defs[defIndex].Id) dtoIndex++; + var cc = content[dtoIndex]; + cc.Properties = propertyData[cc.Id]; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity)cc).ResetDirtyProperties(false); + + defIndex++; + } + } /// /// This builds the Xml document used for the XML cache diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs index be0808bb19..430261bf0e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs @@ -51,8 +51,6 @@ namespace Umbraco.Core.Persistence.Repositories return entity; } - //TODO: Fix N+1 ! - protected override IEnumerable PerformGetAll(params int[] ids) { if (ids.Any()) From f5fe07ffb7d924e28975452cbf5cecb9db93bab4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 14 Nov 2016 16:05:24 +0100 Subject: [PATCH 06/11] U4-6994 - more N+1 and refactoring --- .../Repositories/ContentRepository.cs | 210 ++++++------------ .../Repositories/MediaRepository.cs | 136 ++++++------ .../Repositories/MemberRepository.cs | 107 +++++---- 3 files changed, 201 insertions(+), 252 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 5d8818e1ac..de8d1bdd8b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -1,22 +1,14 @@ using System; using System.Collections.Generic; -using System.Data; using System.Globalization; using System.Linq; -using System.Linq.Expressions; -using System.Net.Http.Headers; -using System.Text; using System.Xml; using System.Xml.Linq; -using Umbraco.Core.Configuration; -using Umbraco.Core.Dynamics; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; - using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; @@ -81,7 +73,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); if (ids.Any()) { - sql.Where("umbracoNode.id in (@ids)", new { ids = ids }); + sql.Where("umbracoNode.id in (@ids)", new { ids }); } //we only want the newest ones with this method @@ -228,7 +220,7 @@ namespace Umbraco.Core.Persistence.Repositories private void RebuildXmlStructuresProcessQuery(Func serializer, IQuery query, Transaction tr, int pageSize) { var pageIndex = 0; - var total = long.MinValue; + long total; var processed = 0; do { @@ -239,7 +231,7 @@ namespace Umbraco.Core.Persistence.Repositories // see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982 var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, new Tuple("cmsDocument", "nodeId"), - ProcessQuery, "Path", Direction.Ascending, true); + sql => ProcessQuery(sql), "Path", Direction.Ascending, true); var xmlItems = (from descendant in descendants let xml = serializer(descendant) @@ -259,7 +251,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false) .Where(GetBaseWhereClause(), new { Id = id }) .OrderByDescending(x => x.VersionDate, SqlSyntax); - return GetAllBySql(sql); + return ProcessQuery(sql, true); } public override IContent GetByVersion(Guid versionId) @@ -670,63 +662,7 @@ namespace Umbraco.Core.Persistence.Repositories .OrderBy(x => x.Level, SqlSyntax) .OrderBy(x => x.SortOrder, SqlSyntax); - return GetAllBySql(sql); - } - - private IEnumerable GetAllBySql(Sql sql) - { - var dtos = Database.Fetch(sql); - var content = new IContent[dtos.Count]; - var defs = new List(); - - for (var i = 0; i < dtos.Count; i++) - { - var dto = dtos[i]; - - // if the cache contains the published version, use it - var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); - if (cached != null && cached.Published) - { - content[i] = cached; - continue; - } - - // else, need to fetch the version from the database - var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); - var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); - var c = factory.BuildEntity(dto); - if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) - c.Template = _templateRepository.Get(dto.TemplateId.Value); - content[i] = c; - - defs.Add(new DocumentDefinition( - dto.NodeId, - dto.VersionId, - dto.ContentVersionDto.VersionDate, - dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, - contentType - )); - } - - // going to load all properties for all docs from database, - // but at least in one queries thus avoiding N+1 - var propertyData = GetPropertyCollection(sql, defs); - - var dtoIndex = 0; - var defIndex = 0; - while (true) - { - if (defIndex == defs.Count) return content; - while (dtoIndex < dtos.Count && dtos[dtoIndex].NodeId != defs[defIndex].Id) dtoIndex++; - var cc = content[dtoIndex]; - cc.Properties = propertyData[cc.Id]; - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)cc).ResetDirtyProperties(false); - - defIndex++; - } + return ProcessQuery(sql, true); } /// @@ -902,7 +838,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder"; return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, new Tuple("cmsDocument", "nodeId"), - ProcessQuery, orderBy, orderDirection, orderBySystemField, + sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField, filterCallback); } @@ -933,83 +869,79 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder"; return base.GetDatabaseFieldNameForOrderBy(orderBy); } - private IEnumerable ProcessQuery(Sql sql) + private IEnumerable ProcessQuery(Sql sql, bool withCache = false) { - //NOTE: This doesn't allow properties to be part of the query + // fetch returns a list so it's ok to iterate it in this method var dtos = Database.Fetch(sql); + if (dtos.Count == 0) return Enumerable.Empty(); - //nothing found - if (dtos.Any() == false) return Enumerable.Empty(); + var content = new IContent[dtos.Count]; + var defs = new List(); + var templateIds = new List(); - //content types - //NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types - var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray()) - .ToArray(); - - - var ids = dtos - .Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0) - .Select(x => x.TemplateId.Value).ToArray(); - - //NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types - var templates = ids.Length == 0 ? Enumerable.Empty() : _templateRepository.GetAll(ids).ToArray(); - - var dtosWithContentTypes = dtos - //This select into and null check are required because we don't have a foreign damn key on the contentType column - // http://issues.umbraco.org/issue/U4-5503 - .Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) }) - .Where(x => x.contentType != null) - .ToArray(); - - //Go get the property data for each document - var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition( - d.dto.NodeId, - d.dto.VersionId, - d.dto.ContentVersionDto.VersionDate, - d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, - d.contentType)); - - var propertyData = GetPropertyCollection(sql, docDefs); - - return dtosWithContentTypes.Select(d => CreateContentFromDto( - d.dto, - contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId), - templates.FirstOrDefault(tem => tem.Id == (d.dto.TemplateId.HasValue ? d.dto.TemplateId.Value : -1)), - propertyData[d.dto.NodeId])); - } - - /// - /// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion. - /// - /// - /// - /// - /// - /// - private IContent CreateContentFromDto(DocumentDto dto, - IContentType contentType, - ITemplate template, - Models.PropertyCollection propCollection) - { - var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); - var content = factory.BuildEntity(dto); - - //Check if template id is set on DocumentDto, and get ITemplate if it is. - if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) + for (var i = 0; i < dtos.Count; i++) { - content.Template = template ?? _templateRepository.Get(dto.TemplateId.Value); - } - else - { - //ensure there isn't one set. - content.Template = null; + var dto = dtos[i]; + + // if the cache contains the published version, use it + if (withCache) + { + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null && cached.Published) + { + content[i] = cached; + continue; + } + } + + // else, need to fetch from the database + // content type repository is full-cache so OK to get each one independently + var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); + content[i] = factory.BuildEntity(dto); + + // need template + if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) + templateIds.Add(dto.TemplateId.Value); + + // need properties + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.VersionId, + dto.ContentVersionDto.VersionDate, + dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, + contentType + )); } - content.Properties = propCollection; + // load all required templates in 1 query + var templates = _templateRepository.GetAll(templateIds.ToArray()) + .ToDictionary(x => x.Id, x => x); + + // load all properties for all documents from database in 1 query + var propertyData = GetPropertyCollection(sql, defs); + + // assign + var dtoIndex = 0; + foreach (var def in defs) + { + // move to corresponding item (which has to exist) + while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++; + + // complete the item + var cc = content[dtoIndex]; + var dto = dtos[dtoIndex]; + ITemplate template = null; + if (dto.TemplateId.HasValue) + templates.TryGetValue(dto.TemplateId.Value, out template); // else null + cc.Template = template; + cc.Properties = propertyData[cc.Id]; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity) cc).ResetDirtyProperties(false); + } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)content).ResetDirtyProperties(false); return content; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 598c9e912d..48372e5517 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -4,21 +4,17 @@ using System.Globalization; using System.Linq; using System.Text; using System.Xml.Linq; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Dynamics; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; - +using Umbraco.Core.Cache; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Repositories { @@ -137,6 +133,74 @@ namespace Umbraco.Core.Persistence.Repositories #region Overrides of VersionableRepositoryBase + public override IEnumerable GetAllVersions(int id) + { + var sql = GetBaseQuery(false) + .Where(GetBaseWhereClause(), new { Id = id }) + .OrderByDescending(x => x.VersionDate, SqlSyntax); + return ProcessQuery(sql, true); + } + + private IEnumerable ProcessQuery(Sql sql, bool withCache = false) + { + // fetch returns a list so it's ok to iterate it in this method + var dtos = Database.Fetch(sql); + var content = new IMedia[dtos.Count]; + var defs = new List(); + + for (var i = 0; i < dtos.Count; i++) + { + var dto = dtos[i]; + + // if the cache contains the item, use it + if (withCache) + { + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null) + { + content[i] = cached; + continue; + } + } + + // else, need to fetch from the database + // content type repository is full-cache so OK to get each one independently + var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId); + content[i] = factory.BuildEntity(dto); + + // need properties + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.VersionId, + dto.VersionDate, + dto.ContentDto.NodeDto.CreateDate, + contentType + )); + } + + // load all properties for all documents from database in 1 query + var propertyData = GetPropertyCollection(sql, defs); + + // assign + var dtoIndex = 0; + foreach (var def in defs) + { + // move to corresponding item (which has to exist) + while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++; + + // complete the item + var cc = content[dtoIndex]; + cc.Properties = propertyData[cc.Id]; + + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity) cc).ResetDirtyProperties(false); + } + + return content; + } + public override IMedia GetByVersion(Guid versionId) { var sql = GetBaseQuery(false); @@ -175,7 +239,7 @@ namespace Umbraco.Core.Persistence.Repositories var subQuery = new Sql() .Select("id") .From(SqlSyntax) - .Where(x => x.NodeObjectType == NodeObjectTypeId); + .Where(x => x.NodeObjectType == NodeObjectTypeId); var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery); Database.Execute(deleteSql); @@ -191,7 +255,7 @@ namespace Umbraco.Core.Persistence.Repositories .Where(x => x.NodeObjectType == NodeObjectTypeId); var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery); - Database.Execute(deleteSql); + Database.Execute(deleteSql); } //now insert the data, again if something fails here, the whole transaction is reversed @@ -460,67 +524,11 @@ namespace Umbraco.Core.Persistence.Repositories return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, new Tuple("cmsContentVersion", "contentId"), - ProcessQuery, orderBy, orderDirection, orderBySystemField, + sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField, filterCallback); } - private IEnumerable ProcessQuery(Sql sql) - { - //NOTE: This doesn't allow properties to be part of the query - var dtos = Database.Fetch(sql); - - var ids = dtos.Select(x => x.ContentDto.ContentTypeId).ToArray(); - - //content types - var contentTypes = ids.Length == 0 ? Enumerable.Empty() : _mediaTypeRepository.GetAll(ids).ToArray(); - - var dtosWithContentTypes = dtos - //This select into and null check are required because we don't have a foreign damn key on the contentType column - // http://issues.umbraco.org/issue/U4-5503 - .Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentDto.ContentTypeId) }) - .Where(x => x.contentType != null) - .ToArray(); - - //Go get the property data for each document - var docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition( - d.dto.NodeId, - d.dto.VersionId, - d.dto.VersionDate, - d.dto.ContentDto.NodeDto.CreateDate, - d.contentType)) - .ToArray(); - - var propertyData = GetPropertyCollection(sql, docDefs); - - return dtosWithContentTypes.Select(d => CreateMediaFromDto( - d.dto, - contentTypes.First(ct => ct.Id == d.dto.ContentDto.ContentTypeId), - propertyData[d.dto.NodeId])); - } - - /// - /// Private method to create a media object from a ContentDto - /// - /// - /// - /// - /// - private IMedia CreateMediaFromDto(ContentVersionDto dto, - IMediaType contentType, - PropertyCollection propCollection) - { - var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId); - var media = factory.BuildEntity(dto); - - media.Properties = propCollection; - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)media).ResetDirtyProperties(false); - return media; - } - /// /// Private method to create a media object from a ContentDto /// diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 216fc223db..4d8b19e522 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -2,24 +2,19 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Linq.Expressions; using System.Text; using System.Xml.Linq; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; - +using Umbraco.Core.Cache; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Relators; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Dynamics; namespace Umbraco.Core.Persistence.Repositories { @@ -380,6 +375,14 @@ namespace Umbraco.Core.Persistence.Repositories #region Overrides of VersionableRepositoryBase + public override IEnumerable GetAllVersions(int id) + { + var sql = GetBaseQuery(false) + .Where(GetBaseWhereClause(), new { Id = id }) + .OrderByDescending(x => x.VersionDate, SqlSyntax); + return ProcessQuery(sql, true); + } + public void RebuildXmlStructures(Func serializer, int groupSize = 5000, IEnumerable contentTypeIds = null) { @@ -645,7 +648,7 @@ namespace Umbraco.Core.Persistence.Repositories return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, new Tuple("cmsMember", "nodeId"), - ProcessQuery, orderBy, orderDirection, orderBySystemField, + sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField, filterCallback); } @@ -685,59 +688,65 @@ namespace Umbraco.Core.Persistence.Repositories return base.GetEntityPropertyNameForOrderBy(orderBy); } - private IEnumerable ProcessQuery(Sql sql) + private IEnumerable ProcessQuery(Sql sql, bool withCache = false) { - //NOTE: This doesn't allow properties to be part of the query + // fetch returns a list so it's ok to iterate it in this method var dtos = Database.Fetch(sql); - var ids = dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray(); + var content = new IMember[dtos.Count]; + var defs = new List(); - //content types - var contentTypes = ids.Length == 0 ? Enumerable.Empty() : _memberTypeRepository.GetAll(ids).ToArray(); + for (var i = 0; i < dtos.Count; i++) + { + var dto = dtos[i]; - var dtosWithContentTypes = dtos - //This select into and null check are required because we don't have a foreign damn key on the contentType column - // http://issues.umbraco.org/issue/U4-5503 - .Select(x => new { dto = x, contentType = contentTypes.FirstOrDefault(ct => ct.Id == x.ContentVersionDto.ContentDto.ContentTypeId) }) - .Where(x => x.contentType != null) - .ToArray(); + // if the cache contains the item, use it + if (withCache) + { + var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + if (cached != null) + { + content[i] = cached; + continue; + } + } - //Go get the property data for each document - IEnumerable docDefs = dtosWithContentTypes.Select(d => new DocumentDefinition( - d.dto.NodeId, - d.dto.ContentVersionDto.VersionId, - d.dto.ContentVersionDto.VersionDate, - d.dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, - d.contentType)); + // else, need to fetch from the database + // content type repository is full-cache so OK to get each one independently + var contentType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.NodeId); + content[i] = factory.BuildEntity(dto); - var propertyData = GetPropertyCollection(sql, docDefs); + // need properties + defs.Add(new DocumentDefinition( + dto.NodeId, + dto.ContentVersionDto.VersionId, + dto.ContentVersionDto.VersionDate, + dto.ContentVersionDto.ContentDto.NodeDto.CreateDate, + contentType + )); + } - return dtosWithContentTypes.Select(d => CreateMemberFromDto( - d.dto, - contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId), - propertyData[d.dto.NodeId])); - } + // load all properties for all documents from database in 1 query + var propertyData = GetPropertyCollection(sql, defs); - /// - /// Private method to create a member object from a MemberDto - /// - /// - /// - /// - /// - private IMember CreateMemberFromDto(MemberDto dto, - IMemberType contentType, - PropertyCollection propCollection) - { - var factory = new MemberFactory(contentType, NodeObjectTypeId, dto.ContentVersionDto.NodeId); - var member = factory.BuildEntity(dto); + // assign + var dtoIndex = 0; + foreach (var def in defs) + { + // move to corresponding item (which has to exist) + while (dtos[dtoIndex].NodeId != def.Id) dtoIndex++; - member.Properties = propCollection; + // complete the item + var cc = content[dtoIndex]; + cc.Properties = propertyData[cc.Id]; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)member).ResetDirtyProperties(false); - return member; + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + ((Entity)cc).ResetDirtyProperties(false); + } + + return content; } /// From cfc1704c64537d30ed9de512f3c09e45ffeb251e Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 14 Nov 2016 18:33:16 +0100 Subject: [PATCH 07/11] Fix build after merge --- src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 42f3594478..b735954b6d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -209,6 +209,7 @@ namespace Umbraco.Core.Persistence.Repositories } baseId = xmlItems.Last().NodeId; } + } public override IEnumerable GetAllVersions(int id) { From e499f2d1489a76d57a3896afe5050d9a9bc25e58 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 15 Nov 2016 13:44:44 +0100 Subject: [PATCH 08/11] U4-9108 - add missing change --- src/Umbraco.Core/Sync/ApplicationUrlHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs index a417ec601a..7b713374aa 100644 --- a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs +++ b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs @@ -114,7 +114,8 @@ namespace Umbraco.Core.Sync // - contain a scheme // - end or not with a slash, it will be taken care of // eg "http://www.mysite.com/umbraco" - var registrar = ServerRegistrarResolver.Current.Registrar as IServerRegistrar2; + var resolver = ServerRegistrarResolver.HasCurrent ? ServerRegistrarResolver.Current : null; + var registrar = resolver == null ? null : resolver.Registrar as IServerRegistrar2; url = registrar == null ? null : registrar.GetCurrentServerUmbracoApplicationUrl(); if (url.IsNullOrWhiteSpace() == false) { From 95f5773046b56c42789aca3280abb33ba18aea97 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Nov 2016 08:23:53 +0100 Subject: [PATCH 09/11] U4-9188 Remove unnecessary duplicate index IX_cmsPropertyData --- .../Models/Rdbms/PropertyDataDto.cs | 1 - .../RemovePropertyDataIdIndex.cs | 38 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs index c4cd28f6e0..e9c1685bb3 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs @@ -11,7 +11,6 @@ namespace Umbraco.Core.Models.Rdbms { [Column("id")] [PrimaryKeyColumn] - [Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyData")] public int Id { get; set; } [Column("contentNodeId")] diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs new file mode 100644 index 0000000000..b50c8e5f94 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero +{ + /// + /// See: http://issues.umbraco.org/issue/U4-9188 + /// + [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + public class UpdateUniqueIndexOnCmsPropertyData : MigrationBase + { + public UpdateUniqueIndexOnCmsPropertyData(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { + } + + public override void Up() + { + //tuple = tablename, indexname, columnname, unique + var indexes = SqlSyntax.GetDefinedIndexes(Context.Database).ToArray(); + var found = indexes.FirstOrDefault( + x => x.Item1.InvariantEquals("cmsPropertyData") + && x.Item2.InvariantEquals("IX_cmsPropertyData")); + + if (found != null) + { + //drop the index + Delete.Index("IX_cmsPropertyData").OnTable("cmsPropertyData"); + } + } + + public override void Down() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d76cad8a1c..9dff5aa050 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -440,6 +440,7 @@ + From 568a4987e398183e7c4cc92a0ace41e9b58045b8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Nov 2016 17:32:29 +0100 Subject: [PATCH 10/11] U4-9195 - fix PetaPoco page of int --- src/Umbraco.Core/Persistence/PetaPoco.cs | 19 ++++++++++++--- .../SqlSyntax/SqlServerSyntaxProvider.cs | 23 ++++++++----------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index 63278889a2..88ff456ff6 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -739,6 +739,11 @@ namespace Umbraco.Core.Persistence /// internal virtual void BuildSqlDbSpecificPagingQuery(DBType databaseType, long skip, long take, string sql, string sqlSelectRemoved, string sqlOrderBy, ref object[] args, out string sqlPage) { + // this is overriden in UmbracoDatabase, and if running SqlServer >=2012, the database type + // is switched from SqlServer to SqlServerCE in order to use the better paging syntax that + // SqlCE supports, and SqlServer >=2012 too. + // so the first case is actually for SqlServer <2012, and second case is CE *and* SqlServer >=2012 + if (databaseType == DBType.SqlServer || databaseType == DBType.Oracle) { sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, ""); @@ -746,8 +751,16 @@ namespace Umbraco.Core.Persistence { sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner"; } - sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}", - sqlOrderBy == null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1); + + // split to ensure that peta_rn is the last field to be selected, else Page would fail + // the resulting sql is not perfect, NPoco has a much nicer way to do it, but it would require + // importing large parts of NPoco + var pos = sqlSelectRemoved.IndexOf("FROM"); + var sqlColumns = sqlSelectRemoved.Substring(0, pos); + var sqlFrom = sqlSelectRemoved.Substring(pos); + + sqlPage = string.Format("SELECT * FROM (SELECT {0}, ROW_NUMBER() OVER ({1}) peta_rn {2}) peta_paged WHERE peta_rn>@{3} AND peta_rn<=@{4}", + sqlColumns, sqlOrderBy ?? "ORDER BY (SELECT NULL)", sqlFrom, args.Length, args.Length + 1); args = args.Concat(new object[] { skip, skip + take }).ToArray(); } else if (databaseType == DBType.SqlServerCE) @@ -774,7 +787,7 @@ namespace Umbraco.Core.Persistence throw new Exception("Unable to parse SQL statement for paged query"); if (_dbType == DBType.Oracle && sqlSelectRemoved.StartsWith("*")) throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id"); - + BuildSqlDbSpecificPagingQuery(_dbType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage); } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 4c35c90e1b..6cc97bdfb3 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -6,16 +6,11 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Persistence.SqlSyntax { /// - /// Represents an SqlSyntaxProvider for Sql Server + /// Represents an SqlSyntaxProvider for Sql Server. /// - [SqlSyntaxProviderAttribute(Constants.DatabaseProviders.SqlServer)] + [SqlSyntaxProvider(Constants.DatabaseProviders.SqlServer)] public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase { - public SqlServerSyntaxProvider() - { - - } - /// /// Gets/sets the version of the current SQL server instance /// @@ -31,7 +26,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax switch (firstPart) { case "13": - _versionName = SqlServerVersionName.V2014; + _versionName = SqlServerVersionName.V2016; break; case "12": _versionName = SqlServerVersionName.V2014; @@ -75,7 +70,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax { var items = db.Fetch("SELECT TableName = t.Name,ColumnName = c.Name,dc.Name,dc.[Definition] FROM sys.tables t INNER JOIN sys.default_constraints dc ON t.object_id = dc.parent_object_id INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND c.column_id = dc.parent_column_id"); return items.Select(x => new Tuple(x.TableName, x.ColumnName, x.Name, x.Definition)); - } + } public override IEnumerable GetTablesInSchema(Database db) { @@ -120,9 +115,9 @@ from sys.tables as T inner join sys.indexes as I on T.[object_id] = I.[object_id inner join sys.all_columns as AC on IC.[object_id] = AC.[object_id] and IC.[column_id] = AC.[column_id] WHERE I.name NOT LIKE 'PK_%' order by T.name, I.name"); - return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, + return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, item.UNIQUE == 1)).ToList(); - + } public override bool DoesTableExist(Database db, string tableName) @@ -164,7 +159,7 @@ order by T.name, I.name"); switch (systemMethod) { case SystemMethods.NewGuid: - return "NEWID()"; + return "NEWID()"; case SystemMethods.CurrentDateTime: return "GETDATE()"; //case SystemMethods.NewSequentialId: @@ -181,11 +176,11 @@ order by T.name, I.name"); get { return "ALTER TABLE [{0}] DROP CONSTRAINT [DF_{0}_{1}]"; } } - + public override string DropIndex { get { return "DROP INDEX {0} ON {1}"; } } public override string RenameColumn { get { return "sp_rename '{0}.{1}', '{2}', 'COLUMN'"; } } - + } } \ No newline at end of file From 0a883ae1091cbc31c9587016eb91ec442f5a94ee Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 16 Nov 2016 18:54:42 +0100 Subject: [PATCH 11/11] Make sure Umbraco runs in Visual Studio 2017 --- .gitignore | 1 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 + src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/umbraco.presentation.targets | 4 ++++ src/umbraco.sln | 2 ++ 5 files changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 5b5e7660c5..16994e0ab1 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,4 @@ build/docs.zip build/ui-docs.zip build/csharp-docs.zip build/msbuild.log +.vs/ diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index e2041e646e..791dd9b270 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2398,6 +2398,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v15.0 diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fef404067f..cf188dab8e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -2250,6 +2250,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v15.0 diff --git a/src/umbraco.presentation.targets b/src/umbraco.presentation.targets index c38a1da485..2a33705d6f 100644 --- a/src/umbraco.presentation.targets +++ b/src/umbraco.presentation.targets @@ -55,6 +55,10 @@ $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll + + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll +