From 62b6efc4da3c7c6ec80d1b8070ca17a8b785609b Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 30 Mar 2018 10:16:21 +0200 Subject: [PATCH] Fix upgrading --- build/build.ps1 | 2 +- src/Umbraco.Core/Persistence/Dtos/UserDto.cs | 2 +- .../Persistence/IUmbracoDatabaseFactory.cs | 5 + .../Persistence/NPocoSqlExtensions.cs | 9 +- .../Persistence/UmbracoDatabaseFactory.cs | 22 ++- .../Persistence/UmbracoPocoDataBuilder.cs | 44 ++++++ src/Umbraco.Core/Runtime/CoreRuntime.cs | 7 + src/Umbraco.Core/Umbraco.Core.csproj | 3 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Tests.Benchmarks/App.config | 130 ------------------ .../Umbraco.Tests.Benchmarks.csproj | 22 +-- src/Umbraco.Tests.Benchmarks/app.config | 4 +- src/Umbraco.Tests.Benchmarks/packages.config | 8 +- src/Umbraco.Tests/Testing/TestDatabase.cs | 40 ++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +- src/Umbraco.Tests/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 36 ++--- src/Umbraco.Web.UI/packages.config | 10 +- .../Install/InstallSteps/UpgradeStep.cs | 16 ++- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 20 files changed, 177 insertions(+), 193 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/UmbracoPocoDataBuilder.cs delete mode 100644 src/Umbraco.Tests.Benchmarks/App.config diff --git a/build/build.ps1 b/build/build.ps1 index 20eeb1a8ea..6721bc4403 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -30,7 +30,7 @@ @{ Continue = $continue }) if ($ubuild.OnError()) { return } - Write-Host "Zbu.ModelsBuilder Build" + Write-Host "Umbraco Cms Build" Write-Host "Umbraco.Build v$($ubuild.BuildVersion)" # ################################################################ diff --git a/src/Umbraco.Core/Persistence/Dtos/UserDto.cs b/src/Umbraco.Core/Persistence/Dtos/UserDto.cs index 340db50767..c6cc889ab0 100644 --- a/src/Umbraco.Core/Persistence/Dtos/UserDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/UserDto.cs @@ -107,7 +107,7 @@ namespace Umbraco.Core.Persistence.Dtos /// /// A Json blob stored for recording tour data for a user /// - [Column("tourData")] // FIXME CANNOT UPGRADE??? + [Column("tourData")] [NullSetting(NullSetting = NullSettings.Null)] [SpecialDbType(SpecialDbTypes.NTEXT)] public string TourData { get; set; } diff --git a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs index c02d956a61..9ef320e187 100644 --- a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs @@ -32,5 +32,10 @@ namespace Umbraco.Core.Persistence /// Gets the Sql context. /// ISqlContext SqlContext { get; } + + /// + /// Configures the database factory for upgrades. + /// + void ConfigureForUpgrade(); } } diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index 0c85e57e31..7b12e2e69d 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -888,7 +888,7 @@ namespace Umbraco.Core.Persistence #region Utilities - private static object[] GetColumns(this Sql sql, string tableAlias = null, string referenceName = null, Expression>[] columnExpressions = null, bool withAlias = true) + private static string[] GetColumns(this Sql sql, string tableAlias = null, string referenceName = null, Expression>[] columnExpressions = null, bool withAlias = true) { var pd = sql.SqlContext.PocoDataFactory.ForType(typeof (TDto)); var tableName = tableAlias ?? pd.TableInfo.TableName; @@ -923,10 +923,9 @@ namespace Umbraco.Core.Persistence return withAlias ? (string.IsNullOrEmpty(column.ColumnAlias) ? column.MemberInfoKey : column.ColumnAlias) : null; } - return queryColumns.Select(x => (object) GetColumn(sql.SqlContext.DatabaseType, - tableName, x.Value.ColumnName, - GetAlias(x.Value), - referenceName)).ToArray(); + return queryColumns + .Select(x => GetColumn(sql.SqlContext.DatabaseType, tableName, x.Value.ColumnName, GetAlias(x.Value), referenceName)) + .ToArray(); } private static string GetColumn(DatabaseType dbType, string tableName, string columnName, string columnAlias, string referenceName = null) diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index d038934fed..efe05e951d 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -42,8 +42,10 @@ namespace Umbraco.Core.Persistence private ISqlSyntaxProvider _sqlSyntax; private RetryPolicy _connectionRetryPolicy; private RetryPolicy _commandRetryPolicy; + private NPoco.MapperCollection _pocoMappers; + private bool _upgrading; - #region Ctor + #region Constructors /// /// Initializes a new instance of the . @@ -112,12 +114,14 @@ namespace Umbraco.Core.Persistence /// public bool CanConnect => Configured && DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName); - #region IDatabaseContext - /// public ISqlContext SqlContext => _sqlContext; - #endregion + /// + public void ConfigureForUpgrade() + { + _upgrading = true; + } /// public void Configure(string connectionString, string providerName) @@ -150,13 +154,13 @@ namespace Umbraco.Core.Persistence // ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database // so that everything NPoco is properly cached for the lifetime of the application - var mappers = new NPoco.MapperCollection { new PocoMapper() }; - var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init()); + _pocoMappers = new NPoco.MapperCollection { new PocoMapper() }; + var factory = new FluentPocoDataFactory(GetPocoDataFactoryResolver); _pocoDataFactory = factory; var config = new FluentConfig(xmappers => factory); // create the database factory - _npocoDatabaseFactory = NPoco.DatabaseFactory.Config(x => x + _npocoDatabaseFactory = DatabaseFactory.Config(x => x .UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances .WithFluentConfig(config)); // with proper configuration @@ -177,6 +181,10 @@ namespace Umbraco.Core.Persistence return (IUmbracoDatabase) _npocoDatabaseFactory.GetDatabase(); } + // gets initialized poco data builders + private InitializedPocoDataBuilder GetPocoDataFactoryResolver(Type type, IPocoDataFactory factory) + => new UmbracoPocoDataBuilder(type, _pocoMappers, _upgrading).Init(); + // gets the sql syntax provider that corresponds, from attribute private ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName) { diff --git a/src/Umbraco.Core/Persistence/UmbracoPocoDataBuilder.cs b/src/Umbraco.Core/Persistence/UmbracoPocoDataBuilder.cs new file mode 100644 index 0000000000..db99d55b3d --- /dev/null +++ b/src/Umbraco.Core/Persistence/UmbracoPocoDataBuilder.cs @@ -0,0 +1,44 @@ +using System; +using System.Reflection; +using NPoco; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Persistence +{ + /// + /// Umbraco's implementation of NPoco . + /// + /// + /// NPoco PocoDataBuilder analyzes DTO classes and returns infos about the tables and + /// their columns. + /// In some very special occasions, a class may expose a column that we do not want to + /// use. This is essentially when adding a column to the User table: if the code wants the + /// column to exist, and it does not exist yet in the database, because a given migration has + /// not run, then the user cannot log into the site, and cannot upgrade = catch 22. + /// So far, this is very manual. We don't try to be clever and figure out whether the + /// columns exist already. We just ignore it. + /// Beware, the application MUST restart when this class behavior changes. + /// + internal class UmbracoPocoDataBuilder : PocoDataBuilder + { + private readonly bool _upgrading; + + public UmbracoPocoDataBuilder(Type type, MapperCollection mapper, bool upgrading) + : base(type, mapper) + { + _upgrading = upgrading; + } + + protected override ColumnInfo GetColumnInfo(MemberInfo mi, Type type) + { + var columnInfo = base.GetColumnInfo(mi, type); + + if (_upgrading) + { + if (type == typeof(UserDto) && mi.Name == "TourData") columnInfo.IgnoreColumn = true; + } + + return columnInfo; + } + } +} diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 44d7ad3f68..731f84e20f 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -139,7 +139,14 @@ namespace Umbraco.Core.Runtime { var dbfactory = container.GetInstance(); SetRuntimeStateLevel(dbfactory, Logger); + Logger.Debug($"Runtime level: {_state.Level}"); + + if (_state.Level == RuntimeLevel.Upgrade) + { + Logger.Debug($"Configure database factory for upgrades."); + dbfactory.ConfigureForUpgrade(); + } } catch { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 249fa7e1a9..515d5009c4 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -80,7 +80,7 @@ - + @@ -381,6 +381,7 @@ + diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 2115afadbf..ee4b036177 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -50,7 +50,7 @@ 1.0.0-beta024 - + diff --git a/src/Umbraco.Tests.Benchmarks/App.config b/src/Umbraco.Tests.Benchmarks/App.config deleted file mode 100644 index 8af971b1f8..0000000000 --- a/src/Umbraco.Tests.Benchmarks/App.config +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 8021c71ba8..ac75012977 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -56,15 +56,15 @@ ..\packages\Castle.Core.4.1.1\lib\net45\Castle.Core.dll + + ..\packages\Microsoft.CodeAnalysis.Common.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll + + + ..\packages\Microsoft.CodeAnalysis.CSharp.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll + ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll - - ..\packages\Microsoft.CodeAnalysis.Common.2.4.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll - - - ..\packages\Microsoft.CodeAnalysis.CSharp.2.4.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll - ..\packages\Microsoft.DotNet.InternalAbstractions.1.0.0\lib\net451\Microsoft.DotNet.InternalAbstractions.dll @@ -77,8 +77,8 @@ ..\packages\Moq.4.7.99\lib\net45\Moq.dll - - ..\packages\NPoco.3.6.1\lib\net45\NPoco.dll + + ..\packages\NPoco.3.9.3\lib\net45\NPoco.dll @@ -208,6 +208,10 @@ Umbraco.Web + + + + @@ -234,4 +238,4 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" --> - + \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/app.config b/src/Umbraco.Tests.Benchmarks/app.config index 8af971b1f8..18bad6619c 100644 --- a/src/Umbraco.Tests.Benchmarks/app.config +++ b/src/Umbraco.Tests.Benchmarks/app.config @@ -119,11 +119,11 @@ - + - + diff --git a/src/Umbraco.Tests.Benchmarks/packages.config b/src/Umbraco.Tests.Benchmarks/packages.config index 438fb232b4..37c28f9569 100644 --- a/src/Umbraco.Tests.Benchmarks/packages.config +++ b/src/Umbraco.Tests.Benchmarks/packages.config @@ -5,16 +5,16 @@ - - - + + + - + diff --git a/src/Umbraco.Tests/Testing/TestDatabase.cs b/src/Umbraco.Tests/Testing/TestDatabase.cs index 21e647f2fe..b1ddbd08d4 100644 --- a/src/Umbraco.Tests/Testing/TestDatabase.cs +++ b/src/Umbraco.Tests/Testing/TestDatabase.cs @@ -640,6 +640,26 @@ namespace Umbraco.Tests.Testing throw new NotImplementedException(); } + public Task SingleAsync(string sql, params object[] args) + { + throw new NotImplementedException(); + } + + public Task SingleAsync(Sql sql) + { + throw new NotImplementedException(); + } + + public Task SingleOrDefaultAsync(string sql, params object[] args) + { + throw new NotImplementedException(); + } + + public Task SingleOrDefaultAsync(Sql sql) + { + throw new NotImplementedException(); + } + public Task SingleByIdAsync(object primaryKey) { throw new NotImplementedException(); @@ -650,6 +670,26 @@ namespace Umbraco.Tests.Testing throw new NotImplementedException(); } + public Task FirstAsync(string sql, params object[] args) + { + throw new NotImplementedException(); + } + + public Task FirstAsync(Sql sql) + { + throw new NotImplementedException(); + } + + public Task FirstOrDefaultAsync(string sql, params object[] args) + { + throw new NotImplementedException(); + } + + public Task FirstOrDefaultAsync(Sql sql) + { + throw new NotImplementedException(); + } + public Task> QueryAsync(string sql, params object[] args) { throw new NotImplementedException(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 03e06799da..b73555af5e 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -137,8 +137,8 @@ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - - ..\packages\NPoco.3.6.1\lib\net45\NPoco.dll + + ..\packages\NPoco.3.9.3\lib\net45\NPoco.dll ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index f1b5109576..1a1c0f0b2d 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -27,7 +27,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 0bf9e597ca..06915d12fd 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1,7 +1,7 @@  - - + + v4.6.1 @@ -66,14 +66,14 @@ ..\packages\Log4Net.Async.2.0.4\lib\net40\Log4Net.Async.dll - - ..\packages\Microsoft.CodeAnalysis.Common.2.3.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll + + ..\packages\Microsoft.CodeAnalysis.Common.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll - - ..\packages\Microsoft.CodeAnalysis.CSharp.2.3.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll + + ..\packages\Microsoft.CodeAnalysis.CSharp.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.5\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.8\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll @@ -982,15 +982,10 @@ + - - - - - - - - + + 11.0 @@ -1012,7 +1007,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" - True + False True 8000 / @@ -1106,4 +1101,11 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 98b3d56f79..66ae8a6406 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -21,12 +21,12 @@ - - - - + + + + - + diff --git a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs index 1d36fe9e12..7e3bd57524 100644 --- a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs @@ -22,13 +22,17 @@ namespace Umbraco.Web.Install.InstallSteps var currentVersion = UmbracoVersion.Local.ToString(); var newVersion = UmbracoVersion.SemanticVersion.ToString(); + string FormatGuidState(string value) + { + if (string.IsNullOrWhiteSpace(value)) value = "unknown"; + else if (Guid.TryParse(value, out var currentStateGuid)) + value = currentStateGuid.ToString("N").Substring(0, 8); + return value; + } + var state = Current.RuntimeState; // fixme inject - var currentState = state.CurrentMigrationState; - if (string.IsNullOrWhiteSpace(currentState)) currentState = "unknown"; - var newState = state.FinalMigrationState?.Trim('{', '}'); - if (string.IsNullOrWhiteSpace(newState)) newState = "unknown"; - else if (Guid.TryParse(newState, out _)) - newState = newState.Substring(0, 8); + var currentState = FormatGuidState(state.CurrentMigrationState); + var newState = FormatGuidState(state.FinalMigrationState); var reportUrl = $"https://our.umbraco.org/contribute/releases/compare?from={currentVersion}&to={newVersion}¬es=1"; diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 2163a2e078..ecdb2e0552 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -102,7 +102,7 @@ - +