diff --git a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs index e28a0f4c42..3db8e4e0cb 100644 --- a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs @@ -17,6 +17,7 @@ public class ModelsBuilderSettings internal const bool StaticAcceptUnsafeModelsDirectory = false; internal const int StaticDebugLevel = 0; internal const bool StaticIncludeVersionNumberInGeneratedModels = true; + internal const bool StaticGenerateVirtualProperties = true; private bool _flagOutOfDateModels = true; /// @@ -77,4 +78,13 @@ public class ModelsBuilderSettings /// [DefaultValue(StaticIncludeVersionNumberInGeneratedModels)] public bool IncludeVersionNumberInGeneratedModels { get; set; } = StaticIncludeVersionNumberInGeneratedModels; + + /// + /// Gets or sets a value indicating whether to mark all properties in the generated models as virtual. + /// + /// + /// Virtual properties will not work with Hot Reload when running dotnet watch. + /// + [DefaultValue(StaticGenerateVirtualProperties)] + public bool GenerateVirtualProperties { get; set; } = StaticGenerateVirtualProperties; } diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs index e192f329e4..7cf27ebef4 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs @@ -31,21 +31,37 @@ public class ModelsGenerator : IModelsGenerator Directory.CreateDirectory(modelsDirectory); } - foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) - { - File.Delete(file); - } - IList typeModels = _umbracoService.GetAllTypes(); var builder = new TextBuilder(_config, typeModels); + var generatedFiles = new List(); foreach (TypeModel typeModel in builder.GetModelsToGenerate()) { var sb = new StringBuilder(); builder.Generate(sb, typeModel); var filename = Path.Combine(modelsDirectory, typeModel.ClrName + ".generated.cs"); - File.WriteAllText(filename, sb.ToString()); + generatedFiles.Add(filename); + + var code = sb.ToString(); + + // leave the file alone if its contents is identical to the generated model + if (File.Exists(filename) && File.ReadAllText(filename).Equals(code)) + { + continue; + } + + // overwrite the file + File.WriteAllText(filename, code); + } + + // clean up old/leftover generated files + foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) + { + if (generatedFiles.InvariantContains(file) is false) + { + File.Delete(file); + } } // the idea was to calculate the current hash and to add it as an extra file to the compilation, diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs index 875dfaa218..6e2bd6aed9 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs @@ -384,7 +384,11 @@ public class TextBuilder : Builder sb.AppendFormat("\t\t[ImplementPropertyType(\"{0}\")]\n", property.Alias); - sb.Append("\t\tpublic virtual "); + sb.Append("\t\tpublic "); + if (Config.GenerateVirtualProperties) + { + sb.Append("virtual "); + } WriteClrType(sb, property.ClrTypeName); sb.AppendFormat( @@ -460,7 +464,11 @@ public class TextBuilder : Builder if (mixinStatic) { - sb.Append("\t\tpublic virtual "); + sb.Append("\t\tpublic "); + if (Config.GenerateVirtualProperties) + { + sb.Append("virtual "); + } WriteClrType(sb, property.ClrTypeName); sb.AppendFormat( " {0} => {1}(this, _publishedValueFallback);\n", @@ -468,7 +476,11 @@ public class TextBuilder : Builder } else { - sb.Append("\t\tpublic virtual "); + sb.Append("\t\tpublic "); + if (Config.GenerateVirtualProperties) + { + sb.Append("virtual "); + } WriteClrType(sb, property.ClrTypeName); sb.AppendFormat( " {0} => this.Value",