Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial checkin for Reverse Engineering #1601

Closed
wants to merge 65 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
2531c3d
First working copy with EF.Core
lajones Dec 5, 2014
a8da7f3
Updated to use migrations-like command line. Not working as templates…
lajones Jan 9, 2015
7e76737
Working with templates as resources (not output to files though)
lajones Jan 9, 2015
c57e3df
Working with templates as resources (not output to files though) - re…
lajones Jan 9, 2015
6aef649
Working with templates as resources plus now outputs files
lajones Jan 9, 2015
17c8e86
Updated templates to output usings separately and put cstr in context.
lajones Jan 10, 2015
54511ed
Fixed context template so no space between cstr name and parens
lajones Jan 10, 2015
d33887e
Not working - does not understand Count() syntax
lajones Jan 10, 2015
51736f6
Added NavProps to PocoTemplate
lajones Jan 10, 2015
97bfa12
Tidying up code
lajones Jan 11, 2015
867e409
Adding OnConfiguration() and OnModelCreating() to ContextTemplate
lajones Jan 11, 2015
6795dcf
Adding PropertiesCode() and PropertyAttibutesCode() to EntityType Tem…
lajones Jan 12, 2015
df4644b
Adding TestProviderEntityTypeTemplateHelper
lajones Jan 12, 2015
810ed44
Adding ability to add attributes to Navigations
lajones Jan 12, 2015
33ecab7
Dependency is on EntityFramework.Core.
lajones Jan 12, 2015
7c3724a
Returning templates directly as strings (rather than resources) plus …
lajones Jan 12, 2015
f4ba3ae
Reading in SQL Server metadata info for Tables and Columns
lajones Jan 13, 2015
4c62c99
SQL provider using tables and columns to produce EntityTypes with pro…
lajones Jan 13, 2015
7d0cc67
SQL provider updated to use constraint columns to distinguish primary…
lajones Jan 14, 2015
4c52ed8
SQL provider updated to use constraint columns to distinguish primary…
lajones Jan 14, 2015
650fe8e
Adding Fluent definition of keys
lajones Jan 14, 2015
30dfccd
Adding better fluent definition of keys
lajones Jan 14, 2015
2de4254
Adding support for properties with nullable types
lajones Jan 14, 2015
2a4e54a
Adding support for adding ForeignKey annotations to the properties in…
lajones Jan 14, 2015
2812cb1
Small updates
lajones Jan 14, 2015
fd770be
Tidying up plus fixing issue in SplitString()
lajones Jan 15, 2015
56baa23
Set up to use CodeGenerators in Relational instead of the template ap…
lajones Jan 16, 2015
e527cec
Added generation of EntityType code in Relational
lajones Jan 16, 2015
2b73e16
Adding forgotten file plus removing dependencies on templating
lajones Jan 16, 2015
6a792eb
Moving all ReverseEngineering code into Relational
lajones Jan 16, 2015
2b716bc
Removing all deps on EF.ReverseEngineering
lajones Jan 16, 2015
b4454ca
Removing EF.ReverseEngineering and EF.ReverseEngineering.TestProvider…
lajones Jan 16, 2015
cff5c79
Some perf improvements - loop only once over tableConstraintColumns a…
lajones Jan 17, 2015
afadd59
Moving type mapping to utility class
lajones Jan 17, 2015
c373083
Tidying up
lajones Jan 17, 2015
42d6fe5
Tidying up 2
lajones Jan 17, 2015
f9f5824
Added MaxLength Fluent configuration
lajones Jan 20, 2015
48630f6
Updated to use new design where XXXContext objects call out to CSharp…
lajones Jan 21, 2015
dc336cd
Updating code to change names of classes to better reflect what they …
lajones Jan 22, 2015
2a37cdb
Adding ForSqlServer() fluent APIs for EntityType and Property (note: …
lajones Jan 28, 2015
4ce5f95
Making CSharpCodeGeneratorHelper more general
lajones Jan 29, 2015
41c885e
Start of updates to use relational model to produce codegen model
lajones Jan 29, 2015
7743b40
Not working - have updated the IModel Generator to produce an IModel …
lajones Feb 3, 2015
06d1aae
Updated Code Generation code - still need to process FKs.
lajones Feb 4, 2015
96016e8
Processing FKs now included (ManyToOne only)
lajones Feb 5, 2015
d9d71ef
Updated to calculate NavProp names in the MetadataModelProvider, stor…
lajones Feb 5, 2015
bb11415
Adding some useful comments in the generated code.
lajones Feb 5, 2015
c45fb0c
Include definition of the underlying FK columns after the ManyToOne F…
lajones Feb 5, 2015
88cfe04
Using e.g. int instead of Int32. Using underscore in names with chara…
lajones Feb 6, 2015
6acfd7d
Tidying up code.
lajones Feb 6, 2015
1b722b4
Adding ILogger to available services.
lajones Feb 6, 2015
53967d9
Updated based on testing with AdventureWorks. Do not allow codeGenMod…
lajones Feb 7, 2015
5c76f38
Added prevention of using C# keywords for property names. Added suppo…
lajones Feb 9, 2015
3c1f1e4
Tidying up and adding generation of the UseIdentity() fluent API.
lajones Feb 9, 2015
923113b
Making self-referencing relationships be automatically OneToOne.
lajones Feb 10, 2015
7ac0952
Adding ServiceProvider.
lajones Feb 10, 2015
f698fda
Fixing up bad rebase for EF.Commands
lajones Feb 10, 2015
cb5a6f2
Removing bad dependency - bad rebase
lajones Feb 10, 2015
9970516
Updated to use new HasOne().WithMany() or WithOne() for Navigations i…
lajones Feb 10, 2015
101cf97
Moving SqlServer design-time classes to new assembly.
lajones Feb 10, 2015
5fde765
Moving Relational design code to new Relational.Design assembly.
lajones Feb 10, 2015
bd5672f
Using nameof() in Check.NotNull().
lajones Feb 11, 2015
cdb6ee4
Tidying up code.
lajones Feb 11, 2015
0af2fec
Tidying up again.
lajones Feb 12, 2015
4a17eba
Missed ending paren in last checkin.
lajones Feb 12, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions EntityFramework-2014.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EntityFramework.Microbenchm
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EntityFramework.Microbenchmarks.Core", "test\EntityFramework.Microbenchmarks.Core\EntityFramework.Microbenchmarks.Core.kproj", "{A9537086-CA3E-458B-BB72-F4D3F32FE7BE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EntityFramework.ReverseEngineering.FunctionalTests", "test\EntityFramework.ReverseEngineering.FunctionalTests\EntityFramework.ReverseEngineering.FunctionalTests.kproj", "{54E13632-C302-474D-909A-06F6469D96BD}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EntityFramework.SqlServer.Design", "src\EntityFramework.SqlServer.Design\EntityFramework.SqlServer.Design.kproj", "{F62DFDA6-2281-4FDA-A36D-AB83C05A3B51}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EntityFramework.Relational.Design", "src\EntityFramework.Relational.Design\EntityFramework.Relational.Design.kproj", "{5CAE6E4C-73F2-466B-8592-30491FFC97CA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -55,10 +61,6 @@ Global
{11D8B323-91B7-4F3D-9A15-6DC47D1A9B93}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11D8B323-91B7-4F3D-9A15-6DC47D1A9B93}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11D8B323-91B7-4F3D-9A15-6DC47D1A9B93}.Release|Any CPU.Build.0 = Release|Any CPU
{EAD7395F-88DA-4146-90E2-4D4987B0F31B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EAD7395F-88DA-4146-90E2-4D4987B0F31B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAD7395F-88DA-4146-90E2-4D4987B0F31B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAD7395F-88DA-4146-90E2-4D4987B0F31B}.Release|Any CPU.Build.0 = Release|Any CPU
{AD2D13EB-AA07-45FB-B946-1F61CCC0D9F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD2D13EB-AA07-45FB-B946-1F61CCC0D9F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD2D13EB-AA07-45FB-B946-1F61CCC0D9F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -91,10 +93,6 @@ Global
{0BD27265-9890-45FD-97F0-6E05933CDD47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BD27265-9890-45FD-97F0-6E05933CDD47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BD27265-9890-45FD-97F0-6E05933CDD47}.Release|Any CPU.Build.0 = Release|Any CPU
{110BC752-F3FA-46E0-880B-15DCFBF378E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{110BC752-F3FA-46E0-880B-15DCFBF378E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{110BC752-F3FA-46E0-880B-15DCFBF378E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{110BC752-F3FA-46E0-880B-15DCFBF378E2}.Release|Any CPU.Build.0 = Release|Any CPU
{D5B43360-EF76-4029-928F-358FFDA05E5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5B43360-EF76-4029-928F-358FFDA05E5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5B43360-EF76-4029-928F-358FFDA05E5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -123,14 +121,25 @@ Global
{A9537086-CA3E-458B-BB72-F4D3F32FE7BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9537086-CA3E-458B-BB72-F4D3F32FE7BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9537086-CA3E-458B-BB72-F4D3F32FE7BE}.Release|Any CPU.Build.0 = Release|Any CPU
{54E13632-C302-474D-909A-06F6469D96BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54E13632-C302-474D-909A-06F6469D96BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54E13632-C302-474D-909A-06F6469D96BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54E13632-C302-474D-909A-06F6469D96BD}.Release|Any CPU.Build.0 = Release|Any CPU
{F62DFDA6-2281-4FDA-A36D-AB83C05A3B51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F62DFDA6-2281-4FDA-A36D-AB83C05A3B51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F62DFDA6-2281-4FDA-A36D-AB83C05A3B51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F62DFDA6-2281-4FDA-A36D-AB83C05A3B51}.Release|Any CPU.Build.0 = Release|Any CPU
{5CAE6E4C-73F2-466B-8592-30491FFC97CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5CAE6E4C-73F2-466B-8592-30491FFC97CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CAE6E4C-73F2-466B-8592-30491FFC97CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CAE6E4C-73F2-466B-8592-30491FFC97CA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CE8F7D9C-FAC7-4BA9-868F-47A8BA3C381B} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
{11D8B323-91B7-4F3D-9A15-6DC47D1A9B93} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
{EAD7395F-88DA-4146-90E2-4D4987B0F31B} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
{AD2D13EB-AA07-45FB-B946-1F61CCC0D9F7} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
{3D1C73AF-11B3-4B33-81C9-E833DD9C5107} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
{1ACE01A3-97E2-41A7-A315-4AE53E53794E} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
Expand All @@ -139,13 +148,14 @@ Global
{7A27CAE9-9FC6-402F-AEDA-90717EB1365D} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{02C7760F-E138-4D54-AEDE-59CB17B503D9} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{0BD27265-9890-45FD-97F0-6E05933CDD47} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{110BC752-F3FA-46E0-880B-15DCFBF378E2} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{D5B43360-EF76-4029-928F-358FFDA05E5A} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{9A520B14-4E04-4D20-8821-C1C085B64EBA} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{E7FDBBB3-9D5F-4FAB-8515-B02B688F81A1} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{C9370CB1-1295-4163-9BAB-6B5E9A35595B} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{CCEAA6B7-9270-4F5D-8F50-F2773F2A87CE} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{E800A5B3-8620-4826-8CE2-B8BE1F754DD1} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{A9537086-CA3E-458B-BB72-F4D3F32FE7BE} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{54E13632-C302-474D-909A-06F6469D96BD} = {C605BAB7-B06E-4BDF-80B2-E661B7670E25}
{F62DFDA6-2281-4FDA-A36D-AB83C05A3B51} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
{5CAE6E4C-73F2-466B-8592-30491FFC97CA} = {E7D68B03-3A5D-4710-9A1F-20D198E41F78}
EndGlobalSection
EndGlobal
64 changes: 63 additions & 1 deletion src/EntityFramework.Commands/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Commands.Utilities;
using Microsoft.Data.Entity.Relational.Design.ReverseEngineering;
using Microsoft.Data.Entity.Utilities;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.Runtime.Common.CommandLine;

Expand All @@ -17,17 +21,23 @@ namespace Microsoft.Data.Entity.Commands
// TODO: Add verbose option
public class Program
{
public static readonly string _defaultReverseEngineeringProviderAssembly = "EntityFramework.SqlServer.Design";

private readonly IServiceProvider _serviceProvider;
private readonly string _projectDir;
private readonly string _rootNamespace;
private readonly ILibraryManager _libraryManager;
private readonly MigrationTool _migrationTool;
private CommandLineApplication _app;

public Program([NotNull] IApplicationEnvironment appEnv, [NotNull] ILibraryManager libraryManager)
public Program([NotNull] IServiceProvider serviceProvider,
[NotNull] IApplicationEnvironment appEnv, [NotNull] ILibraryManager libraryManager)
{
Check.NotNull(serviceProvider, nameof(serviceProvider));
Check.NotNull(appEnv, nameof(appEnv));
Check.NotNull(libraryManager, nameof(libraryManager));

_serviceProvider = serviceProvider;
_projectDir = appEnv.ApplicationBasePath;
_rootNamespace = appEnv.ApplicationName;

Expand Down Expand Up @@ -161,6 +171,19 @@ public virtual int Main([NotNull] string[] args)
migration.OnExecute(() => ShowHelp(migration.Name));
},
addHelpCommand: false);
_app.Command(
"revEng",
revEng =>
{
revEng.Description = "Command to reverse engineer code from a database";
revEng.HelpOption("-h|--help");
var connectionString = revEng.Argument(
"[connectionString]",
"The connection string of the database");

revEng.OnExecute(() => ReverseEngineer(connectionString.Value));
},
addHelpCommand: false);
_app.Command(
"help",
help =>
Expand Down Expand Up @@ -271,6 +294,35 @@ public virtual int RemoveMigration([CanBeNull] string context)
return 0;
}

public virtual int ReverseEngineer(string connectionString)
{
var providerAssembly = GetReverseEngineerProviderAssembly(_defaultReverseEngineeringProviderAssembly);
if (providerAssembly == null)
{
Console.WriteLine("No provider assembly was found with name " + _defaultReverseEngineeringProviderAssembly);
return 1;
}

var configuration = new ReverseEngineeringConfiguration()
{
ProviderAssembly = providerAssembly,
ConnectionString = connectionString,
OutputPath = _projectDir,
Namespace = _rootNamespace
};

var serviceProvider = new ServiceProvider(_serviceProvider);
var loggerFactory = new LoggerFactory();
var loggerProvider = new LoggerProvider(name => new ConsoleCommandLogger(name, verbose: true));
loggerFactory.AddProvider(loggerProvider);
var logger = loggerFactory.Create<ReverseEngineeringGenerator>();
serviceProvider.AddService(typeof(ILogger), logger);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably add a DatabaseTool class and do this in the constructor the same way as MigrationTool.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I hadn't bothered since the class (unlike MigrationTool) would only have one method, but I can do that. Would probably call it ReverseEngineeringTool though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking it could contain other commands that involve pointing to a database with no context. One example we've talked about is a command to drop the database.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var generator = new ReverseEngineeringGenerator(serviceProvider);
generator.Generate(configuration).Wait();

return 0;
}

public virtual int ShowHelp([CanBeNull] string command)
{
// TODO: Enable multiple parameters in escape sequences
Expand Down Expand Up @@ -325,6 +377,16 @@ private string GetProjectPath(string projectName)

return projectDir;
}

private Assembly GetReverseEngineerProviderAssembly(string providerAssemblyName)
{
return _libraryManager.GetReferencingLibraries("EntityFramework.Relational.Design")
.Distinct()
.Where(l => l.Name == providerAssemblyName)
.SelectMany(l => l.LoadableAssemblies)
.Select((assemblyName, assembly) => Assembly.Load(assemblyName))
.FirstOrDefault();
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/EntityFramework.Commands/Utilities/CommandLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using JetBrains.Annotations;
using Microsoft.Data.Entity.Commands.Migrations;
using Microsoft.Data.Entity.Relational.Migrations;
using Microsoft.Data.Entity.Relational.Design.ReverseEngineering;
using Microsoft.Data.Entity.Utilities;
using Microsoft.Framework.Logging;

Expand All @@ -19,7 +20,8 @@ public abstract class CommandLogger : ILogger
{
typeof(MigrationTool).FullName,
typeof(Migrator).FullName,
typeof(MigrationScaffolder).FullName
typeof(MigrationScaffolder).FullName,
typeof(ReverseEngineeringGenerator).FullName,
};

private readonly string _name;
Expand Down
41 changes: 41 additions & 0 deletions src/EntityFramework.Commands/Utilities/ServiceProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using JetBrains.Annotations;

namespace Microsoft.Data.Entity.Commands.Utilities
{
public class ServiceProvider : IServiceProvider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can DesignTimeServices be reused instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'll look into it. They work slightly differently - this one would use the "design-time" services first and only use the "runtime" services if it can't find the service there. That allows you to override if that's what you want (this was taken from a similar class in Scaffolding). Plus the list of services in DesignTimeServices is hard-coded. But I don't currently use the override functionality and I could hard-code the ILogger service. I won't block on this if that's OK?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. DesignTimeServices is designed the other way around--let the user optionally override design-time services. We may need mechanisms for both.

{
private IServiceProvider _backupServiceProvider;
private Dictionary<Type, object> _localServices = new Dictionary<Type, object>();

public ServiceProvider([CanBeNull]IServiceProvider backupServiceProvider)
{
_backupServiceProvider = backupServiceProvider;
}

public void AddService(Type type, object service)
{
_localServices.Add(type, service);
}

public object GetService(Type serviceType)
{
object service;
if (_localServices.TryGetValue(serviceType, out service))
{
return service;
}

if (_backupServiceProvider != null)
{
return _backupServiceProvider.GetService(serviceType);
}

return null;
}
}
}
1 change: 1 addition & 0 deletions src/EntityFramework.Commands/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
"dependencies": {
"EntityFramework.Relational": "7.0.0-*",
"EntityFramework.Relational.Design": "7.0.0-*",
"Microsoft.CodeAnalysis.CSharp": "1.0.0-rc1-*",
"Microsoft.Framework.DependencyInjection": "1.0.0-*"
},
Expand Down
15 changes: 15 additions & 0 deletions src/EntityFramework.Core/Utilities/IndentedStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ public virtual IndentedStringBuilder AppendLine([NotNull] object o)
return this;
}

public virtual IndentedStringBuilder IncrementIndent()
{
_indent++;
return this;
}

public virtual IndentedStringBuilder DecrementIndent()
{
if (_indent > 0)
{
_indent--;
}
return this;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update Indenter to call these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

public virtual IDisposable Indent()
{
return new Indenter(this);
Expand Down
Loading