Plinth.Scaffold 1.0.25

Prefix Reserved
dotnet tool install --global Plinth.Scaffold --version 1.0.25                
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest # if you are setting up this repo
dotnet tool install --local Plinth.Scaffold --version 1.0.25                
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Plinth.Scaffold&version=1.0.25                
nuke :add-package Plinth.Scaffold --version 1.0.25                

Plinth Scaffold

Overview

Plinth Scaffold is a productivity tool that is used to quickly generate boilerplate code for CRUD operations around a project's business entities. It is primarily meant to be used with the Plinth Common Libraries as well as the Plinth API Starter project, but can also be extended to more specific use-cases if desired. It accepts a JSON file as an input, which defines all business entities of the project, including specific fields and any special behaviors. The output contains generated code at every level of a .NET Core API project, including: SQL table definitions, SQL stored procedures, C# common models, data access classes, logic classes, controller classes, C# API models, and a C# API client. Optionally, this tool can also generate iOS Swift API models and client, Java Retrofit API models and client, as well as Typescript models.

Running Scaffold

Scaffold is installed and ran as a Nuget tool, which can be installed via dotnet.exe. The general syntax for running the Scaffold tool via command-line is the following:

dotnet scaffold [input_file_path] [arguments]

The following example will create the dotnet tool manifest in your project, update Scaffold to the latest available version on nuget.org, and run the tool using a local scaffold.json file as input, and the current folder as output.

@ECHO OFF

IF NOT EXIST .config/dotnet-tools.json (
  ECHO Creating dotnet tool manifest...
  dotnet new tool-manifest
)

dotnet tool update Plinth.Scaffold
dotnet scaffold scaffold.json -o "."
PAUSE

Command-line Arguments

Scaffold supports the following optional command-line arguments:

  • --help - display help screen
  • --version - display version information
  • -o, --output - change where output files are written
  • --disable-database - disable generation of database entities
  • --disable-common - disable generation of common entities
  • --disable-dataaccess - disable generation of data access entities
  • --disable-logic - disable generation of business logic entities
  • --disable-webapi - disable generation of web api entities
  • --webapi-client - generate C# client for web api
  • --web - generate clients for web
  • --mobile-android - generate clients for Android mobile platform (Java)
  • --mobile-ios - generate clients for iOS mobile platform (Swift)
  • --schema - generate json schema for scaffold input file
  • --ignore-cycle-errors - do not throw an error is a cycle is detected in foreign key definitions
  • -q, --quiet - suppress all output
  • -v, --verbose - output scaffolding info

Input File

Scaffold generates its code using a JSON file as an input, which outlines the entire project definition, settings, data model entities and their relationships. Below is a sample of the input file which demonstrates the main sections of the input. Subsequent sections of this document will go into more detail on all available settings and discuss different features supported by the tool.

{
    "name": "TestProject",
	"cSharpNamespace": "Test.Project",
    "database":
    {
        "generateIndividualEntityFiles": true
    },
    "enums": [
        {
            "name": "RoleType",
            "values": [
                "User",
                "Admin"
            ]
        }
    ],
    "entities": [
        {
            "name": "User",
            "pluralName": "Users",
            "fields": [
                {
                    "name": "UserID",
                    "type": "Long",
                    "isPrimaryKey": true
                },
                {
                    "name": "GUID",
                    "type": "Guid",
                    "isApplicationPrimaryKey": true
                },
                {
                    "name": "Username",
                    "type": "String",
                    "isUnique": true
                },
                {
                    "name": "Role",
                    "type": "Enum",
                    "enumName": "RoleType"
                },
                {
                    "name": "DateCreated",
                    "type": "DateTime",
                    "isCurrentDateTime": true
                }
            ]
        }
    ]
}

Root Settings

Name Type Default Description
name string - Name of the project.
cSharpNamespace string - Root namespace of the generated C# classes.
javaPackage string - Java package name of the generated Android (Java) files.
newLine Enum <ul><li>Environment</li><li>Windows</li><li>Unix</li></ul> Environment Newline characters to use for the generated files. Environment-specific newline characters will be used by default.
dbms Enum <ul><li>MSSQL</li><li>PGSQL</li></ul> MSSQL DBMS syntax of the generated database files. MSSQL is the default and has the most feature support at the moment.
databaseConnector Enum <ul><li>Plinth</li><li>Dapper</li></ul> Plinth Database library used in the generated data access classes (DAO) for connecting to the database, and mapping parameters and result sets. Plinth is the default and has the most feature support at the moment.
databaseNamespace string - Namespace of the data access classes when referenced from other generated C# classes. This setting is only used when a custom data access layer needs to be referenced from the generated files, which is usually not the case. By default, the data access namespace will be generated as {cSharpNamespace}.DataAccess.Database.
usePlinthCommon bool true When true, the Plinth Common Libraries namespace will be referenced for utility methods in generated C# classes.<br><br>When false, the project's root namespace will be referenced instead.
useExplicitTypes bool false When true, explicit types will be used when defining local variables in generated C# classes.<br><br>When false, local variable types will be defined implicitly using the var keyword.
useNullableReferenceTypes bool false When true, nullable reference types will have the nullable operator ? appended to them.<br><br>When false, only the nullable value types will use the nullable operator.
useMicrosoftDataSqlClient Obsolete null This field is obsolete, the default is now to use Microsoft.Data.SqlClient
useSystemDataSqlClient bool false When true, data access code will use System.Data.SqlClient instead of Microsoft.Data.SqlClient for communicating with the database.
useJwt bool false When true, Plinth API security code will use the JWT implementation of the security token.<br><br>When false, custom Plinth implementation of the security token will be used instead.
useSwaggerAnnotations bool false When true, Swagger Swashbuckle.AspNetCore.Annotations will be used in C# API models for additional field context, such as designating read-only fields.
loggingFramework Obsolete null This field is obsolete. Only Microsoft.Extensions.Logging is supported
useInjectedLogger bool false When true, the configured logger will be injected as a dependency in C# class constructors.<br><br>When false, a static logger is used via StaticLogManager.GetLogger().
useBaseFolderStructure bool true When true, generated code will be placed in a Base folder on every level (database, data access, logic, etc) and configured for extension and overrides. This allows the Scaffold tool to be re-runnable by cleaning the Base folders on each run and re-generating all code. All custom extensions will be preserved since they are done in separate files and folders. See "Extending Generated Code" section for more details.<br><br>When false, generated code will be placed in the same folders as custom code, requiring custom extensions to be applied directly to the generated files. Because of this limitation, base folder structure is commonly enabled in recent projects.
useManagerSqlTransactions bool false When true, new SQL transactions will be instantiated in the managers instead of controllers.
disableSqlTransactionForReads bool false When true, SQL read operations will not use an explicit transaction.
disableSqlTransactionForSingleWrites bool false When true, SQL write operations that only do a single write will not use an explicit transaction.
useRowLevelSecurity bool false When true, stored procedures will include pre-operation validation checks to make sure that the calling user has access to INSERT, UPDATE, DELETE, and SELECT the desired entity. See "Row-Level Security" section for more details.
systemCallingUserName string - The identifier of the System user that is passed as the CallingUser parameter of generated stored procedures. This is used in row-level security validations for bypassing user-specific security checks, since a System user should have access to all entities of the application.
generateDataAccessInterfaceScaffolds bool false When true, generated data access classes (DAOs) will also have a corresponding interface generated for them in order to make unit testing easier through a mock data layer.
generateWebApiClientScaffolds bool false When true, a C# client for web api will be generated.
generateWebScaffolds bool false When true, a client for web (Typescript) will be generated.
generateMobileAndroidScaffolds bool false When true, a client for Android mobile platform (Java) will be generated.
generateMobileiOSScaffolds bool false When true, a client for iOS mobile platform (Swift) will be generated.
generateGetStoredProcedures bool false When true, individual Get stored procedures will be generated to retrieve an entity by a unique field, instead of re-using the Search stored procedure.
generateBulkInsertMethods bool false When true, methods for inserting lists of entities will be generated in addition to single-record inserts. This is done through the use of table-valued parameters in the bulk insert stored procedures.
generateResetMethods bool false When true, methods for resetting nullable fields back to a null value will be generated.
generateModelColumnAnnotations bool false When true, System.ComponentModel.DataAnnotations.Schema will be used in C# common models for annotating database column names on model fields.
disableAuditFields bool false When true, database audit fields (DateInserted, InsertedBy, DateUpdated, UpdatedBy) will not be generated.
exposeAuditFields bool false When true, database audit fields (DateInserted, InsertedBy, DateUpdated, UpdatedBy) will be exposed in C# common and API models.
database DatabaseSettings - Database-related settings. See "Database Settings" section for more details.
enums List<EnumSettings> - List of project Enums and their values. See "Enum Settings" section for more details.
entities List<EntitySettings> - List of project entities and their settings. See "Entity Settings" section for more details.

Database Settings

Name Type Default Description
tablesFileName string - When generateIndividualEntityFiles is set to false, this will be the name of the single sql file that contains all table definitions. Example: Tables.sql
proceduresFileName string - When generateIndividualEntityFiles is set to false, this will be the name of the single sql file that contains all stored procedure definitions. Example: Procedures.sql
generateIndividualEntityFiles bool false When true, generated database files will be split per entity. For example, if a project contains 8 entities, 8 .sql files will be generated for table definitions, one per each table. 32 .sql files will be generated for stored procedure definitions (8 entities multiplied by 4 stored procedures per entity).<br><br>When false, a single file will be generated for all table definitions and a single file will be generated for all stored procedure definitions.
generateSsdtFiles bool false When true, DacFx library will be used for generating an SSDT database project that includes a merger or all generated and custom database code in the database folder. This project can then be used for automated database deployment. A .dacpac file is also created in this process, and can also me used for deploying schema changes.
ssdtSqlServerVersion Enum <ul><li>Sql90</li><li>Sql100</li><li>SqlAzure</li><li>Sql110</li><li>Sql120</li><li>Sql130</li><li>Sql140</li><li>Sql150</li><li>SqlDw</li><li>Sql160</li></ul> Sql150 When generateSsdtFiles is set to true, this will be the version of SQL Server used for generating the SSDT database project and .dacpac file.
msSqlSchemaName string dbo Custom schema name to be used for generated MSSQL database files.
pgSqlSchemaName string public Custom schema name to be used for generated PGSQL database files.

Enum Settings

Name Type Default Description
name string - Name of the Enum. Referenced by an entity field's enumName setting when the entity field's type is Enum.
isFlagsEnum bool - When true, this Enum will be defined with a FlagsAttribute to indicate that this enumeration can be treated as a set of flags.
forceApiEnumGeneration bool false By default, Enums that are not referenced by any entity fields will not have an API definition generated for them. When this is set to true, an API definition will be generated for this Enum regardless of entity references. This is useful when needing to generate an API definition for an Enum that is not directly tied to the data model.
values List<string> - Values of the Enum.
indexedValues List<EnumIndexedValue> - Indexed values of the Enum. See "Enum Indexed Value Settings" section for more details.

Enum Indexed Value Settings

Name Type Default Description
value string - Value of the Enum.
index string - Optional index value of the Enum.

Entity Settings

Name Type Default Description
name string - Name of the entity.
pluralName string - Plural name of the entity. Used when generating code and documentation that requires referring to the entity in a plural context.
abbreviatedName string - The abbreviated name of the entity that will be as the table alias in generated stored procedures. By default, the abbreviated name is generated using the first letter of every word in the entity's name.
superclassName string - Name of the class that the entity's generated models should extend. This is only used if the project has a custom base class than needs to be the superclass of the generated models.
databaseTableName string - Custom table name to be used for generated database files for this entity.
msSqlSchemaName string - Custom schema name to be used for generated MSSQL database files for this entity.
pgSqlSchemaName string - Custom schema name to be used for generated PGSQL database files for this entity.
useManagerSqlTransactions bool false When true, new SQL transactions will be instantiated in the managers instead of controllers. This is an entity-specific equivalent of the root useManagerSqlTransactions setting.
disableSqlTransactionForReads bool false When true, SQL read operations will not use an explicit transaction. This is an entity-specific equivalent of the root disableSqlTransactionForReads setting.
disableSqlTransactionForSingleWrites bool false When true, SQL write operations that only do a single write will not use an explicit transaction. This is an entity-specific equivalent of the root disableSqlTransactionForSingleWrites setting.
rowLevelSecurityParent string - When useRowLevelSecurity is set to true, this specifies the name of the entity from which the current entity inherits its row-level security definition. See "Row-Level Security" section for more details.
rowLevelSecuritySelectSqlCondition string - An optional SQL statement that will be added as a condition to SELECT stored procedure row-level security code.
rowLevelSecurityModifySqlCondition string - An optional SQL statement that will be added as a condition to INSERT, UPDATE, and DELETE stored procedure row-level security code.
disableRowLevelSecuritySelectSqlGeneration bool false When true, row-level security code will not be generated in the SELECT stored procedure for the entity. This is useful when creating entities that allow public reading of data.
forceApiModelGeneration bool false By default, entities that have controller generation disabled will not have a C# API model generated for them. When this is set to true, C# API model will be generated for this entity regardless of controller generation settings. This is useful for generating C# API models for custom controllers.
disableCommonModelGeneration bool false When true, C# common models will not be generated for this entity. Setting this to true will automatically set disableDAOGeneration, disableManagerGeneration, disableControllerGeneration, and disableApiModelGeneration to true as well.
disableDAOGeneration bool false When true, data access classes will not be generated for this entity. Setting this to true will automatically set disableManagerGeneration, disableControllerGeneration, and disableApiModelGeneration to true as well.
disableManagerGeneration bool false When true, logic classes will not be generated for this entity. Setting this to true will automatically set disableControllerGeneration, and disableApiModelGeneration to true as well.
disableControllerGeneration bool false When true, controller classes will not be generated for this entity. Setting this to true will automatically set disableApiModelGeneration to true as well.
disableApiModelGeneration bool false When true, C# API models will not be generated for this entity.
disableSearchApiGeneration bool false When true, Search endpoints will not be generated in controllers for this entity.
disableGetApiGeneration bool false When true, Get endpoints will not be generated in controllers for this entity.
disableCreateListApiGeneration bool false When true, CreateList endpoints will not be generated in controllers for this entity.
disableCreateApiGeneration bool false When true, Create endpoints will not be generated in controllers for this entity.
disableUpdateApiGeneration bool false When true, Update endpoints will not be generated in controllers for this entity.
disableDeleteApiGeneration bool false When true, Delete endpoints will not be generated in controllers for this entity.
disableReferencedEntityApiGeneration bool false When true, Get and Create endpoints for this entity will not be generated in controllers for entities that this entity references through foreign key definitions.
disableReferencingEntityApiGeneration bool false When true, Get and Create endpoints for entities that reference this entity through foreign key definitions will not be generated in controllers for this entity.
generateGetStoredProcedures bool false When true, individual Get stored procedures will be generated to retrieve an entity by a unique field, instead of re-using the Search stored procedure. This is an entity-specific equivalent of the root generateGetStoredProcedures setting.
generateBulkInsertMethods bool false When true, methods for inserting lists of entities will be generated in addition to single-record inserts for this entity. This is an entity-specific equivalent of the root generateBulkInsertMethods setting.
generateResetMethods bool false When true, methods for resetting nullable fields back to a null value will be generated for this entity. This is an entity-specific equivalent of the root generateResetMethods setting.
disableAuditFields bool false When true, database audit fields (DateInserted, InsertedBy, DateUpdated, UpdatedBy) will not be generated for this entity. This is an entity-specific equivalent of the root disableAuditFields setting.
exposeAuditFields bool false When true, database audit fields (DateInserted, InsertedBy, DateUpdated, UpdatedBy) will be exposed in C# common and API models for this entity. This is an entity-specific equivalent of the root exposeAuditFields setting.
isSystemEntity bool false When useRowLevelSecurity is set to true, this specifies that this entity can only be accessed by the System calling user. See "Row-Level Security" section for more details.
isCallingUserIdentity bool false When useRowLevelSecurity is set to true, this specifies that this entity's GUID represents the identifier of the CallingUser of the current request context. Most typically this is the User entity, and functions as the top-level rowLevelSecurityParent. See "Row-Level Security" section for more details.
isMappingEntity bool false This specifies that this entity represents a mapping table for two entities with a many-to-many relationship. When true, this generates Associate, Delete Association and Get endpoints for the outbound foreign key entities in a mapping entity.
supportsSoftDelete bool false When true, an IsDeleted flag column will be added to the SQL table generated for this entity, and the Delete stored procedure will set the IsDeleted flag instead of deleting the entire row. Search stored procedure will also filter-out records with IsDeleted set.
fields List<EntityField> - A list of all entity fields that make up this entity. See "Entity Field Settings" section for more details.
sortFields List<SortField> - An optional sort field list that specifies the sort order of the Search stored procedure results for this entity. See "Sort Field Settings" section for more details.

Sort Field Settings

Name Type Default Description
name string - Name of the sort field. References an entity field's name setting.
sortDirection Enum <ul><li>Desc</li><li>Asc</li></ul> Desc Sort direction of the sort field.

Entity Field Settings

Name Type Default Description
name string - Name of the entity field.
type Enum <ul><li>Bool</li><li>Enum</li><li>Guid</li><li>Char</li><li>String</li><li>ShortString</li><li>LongString</li><li>Text</li><li>Int</li><li>Long</li><li>Decimal</li><li>Money</li><li>Float</li><li>Date</li><li>Time</li><li>DateTime</li><li>DateTimeOffset</li><li>Binary</li><li>Point</li><li>List</li><li>JsonObject</li><li>Custom</li></ul> - Data type of the entity field. See "Entity Field Type Conversion" section for more details.
defaultValue string - An optional default value of the entity field.
nullIfValue string - An optional value that will set the database column to null when the requested value matches. This can be used to set field values to null on a PATCH request.
isPrimaryKey bool false Specifies whether this field is the primary key for the entity. This field will be used as the database primary key for foreign keys and joins.
isApplicationPrimaryKey bool false Specifies whether this field is the application primary key for the entity. An application primary key is used in stored procedure parameters and results, as well as all layers above (C# code, API clients) to reference the entity. Typically it is a of type Guid in order to not leak any information at the API by exposing auto-increment integer fields, for example.
isUnique bool false Specifies whether this field is unique among all records for the entity. Setting this to true will automatically add a unique constraint in the generated SQL table definition as well as include this field in the generated Search code parameters. Unique fields will also have specific GetBy methods generated for them in order to look up a single record by this field's value. For example, if a User entity has a unique Username field, a GetUserByUsernameAsync method will be generated in C# controllers, logic, and data access classes.
additionalUniqueFields List<string> - In cases when an entity's unique constraint should be specified through a composite unique key of fields, this list of additional entity field name values can be provided. The GetBy methods will still be generated in this case, and will include every field that makes up composite unique key.
isNullable bool false Specifies whether this field is allowed to have null values. This affects SQL table definition and stored procedure joins (INNER JOIN for non-nullable and LEFT OUTER JOIN for nullable fields).
isDbOnly bool false Specifies whether this field is internal to the database and should not be exposed at the application layer.
isEncrypted bool false Specifies whether this field should be encrypted when stored in the database. When true, the Plinth Common Libraries ISecureData utility will be used to encrypt and decrypt this field in the generated data access class.
isSearchField bool false Specifies whether this field should be included in the generated Search code parameters. Setting this to true will not create a database index.
hasIndex bool false Specifies whether this field should have a database index generated for it. Setting this to true will automatically include this field in the generated Search code parameters.
hasUniqueIndex bool false Specifies whether this field should have a database unique index generated for it. Fields with a unique index are treated like fields with isUnique set to true in terms of the code that gets generated.
hasSpatialIndex bool false Specifies whether this field should have a database spatial index generated for it, in cases when this field represents a spatial column. Currently only type Point is supported.
additionalIndexFields List<string> - In cases when an entity's index should be specified through a composite index of fields, this list of additional entity field name values can be provided.
isCallingUserGuid bool false Specifies whether this field represents the id (or Guid) of the CallingUser who inserted the record. Setting this to true will automatically set this field's value to the CallingUser's GUID when inserting a new record in the generated data access classes.
isCurrentDateTime bool false Specifies whether this field represents the current date and time of when the record was inserted. Setting this to true will automatically set this field's value to DateTime.UtcNow when inserting a new record in the generated data access classes.
isBlobGuid bool false Specifies whether this field represents the id (or Guid) of BLOB data. BLOB fields will have upload and download endpoints added for them in the generated controllers, as well as include additional business logic code to manage BLOBs using the Plinth.Storage Library.
isWebApiHidden bool false When true, this field will not be included in the entity's generated C# API models. This setting is used for specifying internal data fields that should not be exposed at the API layer.
enumName string - When type is Enum, this setting references an Enum's name settings for specifying the Enum type of this field.
isUnicode bool false When type is Char, String, ShortString, LongString, or Text, this setting specifies whether unicode characters are allowed in the field's value. This affects the database column type by using NVARCHAR instead of VARCHAR.
length int - When type is Char, String, or Binary, this setting specifies the length of the database column type.
listType string - When type is List, this setting specifies the type of elements in the list. This can reference external types, as long as they are defined in the custom project code. List fields are stored as a Json string in the database column.
jsonObjectType string - When type is JsonObject, this setting specifies the type of the Json object. This can reference external types, as long as they are defined in the custom project code. JsonObject fields are stored as a Json string in the database column.
customFieldType string - When type is Custom, this setting specifies the type of the custom object. This can reference external types, as long as they are defined in the custom project code.
sqlPrecision string - Specifies the custom SQL precision to use for SQL types.
sqlCheckConstraint string - Specifies the custom SQL CHECK constraint to use for the SQL column representing this field.
sqlCalculationStatement string - Specifies the SQL calculation statement to use when this field represents a calculated column. For example, a GEOGRAPHY::Point column can be calculated from Latitude and Longitude values by specifying this setting as GEOGRAPHY::Point(ISNULL(Latitude,0), ISNULL(Longitude,0), 4326) PERSISTED.
foreignKeyEntityName string - When this field is a foreign key, this is the name of the entity that the foreign key references.
foreignKeyEntityAbbreviatedName string - The abbreviated name of the foreign key entity that will be as the table alias in generated stored procedures. By default, the abbreviated name is generated using the first letter of every word in the entity's name.
foreignKeyEntityFieldName string - The name of the entity field that is the primary key of the entity that the foreign key references.
foreignKeyEntityAliasFieldName string - Optional custom name given to the foreign key field in stored procedure results and C# models. By default this field will be named as {ForeignKeyEntity.Name}{ForeignKeyEntityField.Name}.
isForeignKeyOneToOne bool false Specifies whether the foreign key has a 1:1 relationship, in which case this field will not be included in the generated Search code parameters. Get and Create endpoints for this entity will also not be generated in the foreign key entity's controllers.
isForeignKeyCascadeDelete bool false When true, generated database foreign key definition will include an ON DELETE CASCADE to automatically delete this entity when the foreign key entity gets deleted.
additionalForeignKeyFields List<AdditionalForeignKeyField> - Specifying a list of additional fields on the foreign key entity will return these fields in the Search stored procedure results. See "Additional Foreign Key Field Settings" section for more details.
enableExternalUrlGeneration bool false When isBlobGuid is true, setting this to true will add a new property to the generated C# models to return the url for downloading BLOB data. Currently only supports AWS S3 Plinth.Storage provider.
enableUploadApiGeneration bool false When isBlobGuid is true, setting this to true will generate a controller endpoint to upload BLOB data.
enableDownloadApiGeneration bool false When isBlobGuid is true, setting this to true will generate a controller endpoint to download BLOB data.
enableDownloadUrlApiGeneration bool false When isBlobGuid is true, setting this to true will generate a controller endpoint to retrieve a temporary url for downloading BLOB data. Uses the Plinth.Storage Library GetTemporaryUrlReferenceAsync method.
enableLargeFileUploads bool false When isBlobGuid is true, setting this to true will decorated the BLOB upload endpoint with the [DisableRequestSizeLimit] attribute to enable large file uploads.
comment string - An optional comment that will appear above the generated code for this field in table definition and common and API models.

Additional Foreign Key Field Settings

Name Type Default Description
name string - Name of the additional foreign key field. References an entity field's name setting.
aliasName string - Optional custom name given to the foreign key field in stored procedure results and C# models. By default this field will be named as {ForeignKeyEntity.Name}{ForeignKeyEntityField.Name}.

Entity Field Type Conversion

Entity Field Type MSSQL Type PGSQL Type C# Type Java Type Swift Type Typescript Type
Bool BIT BOOLEAN bool Boolean Bool boolean
Enum VARCHAR(50) VARCHAR(50) {enumName} {enumName} {enumName} {enumName}
Guid UNIQUEIDENTIFIER UUID Guid UUID UUID string
Char (N)CHAR({length}) <br> (N)CHAR(255) CHAR({length}) <br> CHAR(255) string String String string
String (N)VARCHAR({length}) <br> (N)VARCHAR(255) VARCHAR({length}) <br> VARCHAR(255) string String String string
ShortString (N)VARCHAR(50) VARCHAR(50) string String String string
LongString (N)VARCHAR(1000) VARCHAR(1000) string String String string
Text (N)VARCHAR(MAX) TEXT string String String string
Int INT SERIAL (isPrimaryKey = true) <br> INT int Integer Int number
Long BIGINT BIGSERIAL (isPrimaryKey = true) <br> BIGINT long Long Int number
Decimal DECIMAL(21,12) DECIMAL(21,12) decimal Double Double number
Money DECIMAL(12,2) DECIMAL(12,2) decimal Double Double number
Float FLOAT FLOAT double Double Double number
Date DATE DATE DateTime Date Date Date
Time TIME(3) TIME TimeSpan Date Date Date
DateTime DATETIME2(3) TIMESTAMP WITH TIME ZONE DateTime Date Date Date
DateTimeOffset DATETIMEOFFSET(3) TIMESTAMP WITH TIME ZONE DateTime Date Date Date
Binary VARBINARY({length}) <br> VARBINARY(MAX) BIT({length}) <br> BYTEA byte[] byte[] [UInt8] Uint8Array
Point GEOGRAPHY::Point GEOGRAPHY::Point string String String string
List NVARCHAR(MAX) TEXT List<{listType}> List<{listType}> [{listType}] {listType}[]
JsonObject NVARCHAR(MAX) JSONB {jsonObjectType} {jsonObjectType} {jsonObjectType} {jsonObjectType}
Custom NVARCHAR(MAX) TEXT {customFieldType} {customFieldType} {customFieldType} {customFieldType}

Row-Level Security

The row-level security code generated by the Scaffold tool relies on the definition of a security hierarchy, starting at the entity that represents the CallingUser and going down to dependent entities. In order to turn on row-level security code generation, the root setting useRowLevelSecurity must be set to true. You will also need to specify the systemCallingUserName to bypass security code checks for System calling users.

Every entity that needs security code generated for it will need to specify one of 3 settings:

  • isSystemEntity - This entity can only be accessed by the System calling user.
  • isCallingUserIdentity - This entity's GUID represents the identifier of the CallingUser of the current request context. Most typically this is the User entity, and functions as the top-level rowLevelSecurityParent.
  • rowLevelSecurityParent - Name of the entity from which the current entity inherits its row-level security definition. This is what sets up the security hierarchy.

Row-level security checks will be generated directly within the stored procedures in order to ensure that any custom business logic that uses the generated data access code will benefit from the automated security checks. The types of checks performed depends on the type of the stored procedure:

  • INSERT - If the entity that is being inserted contains a foreign key to the specified rowLevelSecurityParent, a security check will be generated to make sure the CallingUser has access to the parent entity record. If the entity being inserted contains foreign keys to other entities that have this entity specified as their rowLevelSecurityParent, a security check will also be generated to make sure the CallingUser has access to those entity records as well.
  • UPDATE - A security check will be generated to make sure the CallingUser has access to the entity record being updated. If the entity being updated contains foreign keys to other entities that have this entity specified as their rowLevelSecurityParent, and if the foreign key entity id is provided as a parameter, a security check will also be generated to make sure the CallingUser has access to those entity records as well.
  • DELETE - A security check will be generated to make sure the CallingUser has access to the entity record being deleted.
  • SELECT - A security check will be added to the WHERE clause to make sure the CallingUser has access to each of the returned entity records.

To add more SQL conditions for selecting or modifying an entity and its security children, use rowLevelSecuritySelectSqlCondition and rowLevelSecurityModifySqlCondition.

In order to make an entity that is protected by row-level security code allow public reads, disableRowLevelSecuritySelectSqlGeneration can be set to true.

Extending Generated Code

When the root setting useBaseFolderStructure is set to true, generated code will be placed in a Base folder on every level (database, data access, logic, etc) and configured for extension and overrides. This allows the Scaffold tool to be re-runnable by cleaning the Base folders on each run and re-generating all code. All custom extensions will be preserved since they are done in separate files and folders.

Placing generated code in separate folders also has the advantage of being able to easily distinguish custom code from the generated boilerplate code. For example, the following git command can be used to exclude generated files from the list of changed files, to more easily see the actual business logic that was modified:

git status ':!**/Base/*' ':!**/ContainerBootstrapBase.cs' ':!**/SampleData_Template.sql' ':!**/dbo/*' ':!*.dacpac'

Extending Database Code

When the starter project's DeployDatabase.ps1 script creates a new database, it runs SQL code in Base folders first before executing the code outside of the Base folders on each level. This means that extension .sql files can be created on the same level as the Base folder to modify the base SQL entities. In cases of table definition, the custom SQL file may add new columns, foreign keys, or constraints.

Extending stored procedures is a bit less convenient, since a stored procedure has to be specified in its entirety upon creation or modification. This is why each stored procedure is created using CREATE OR ALTER PROCEDURE command to allow DeployDatabase.ps1 to overwrite existing stored procedures. For any stored procedure that needs to be extended, the base stored procedure .sql file would be copied outside of the Procedures\Base folder, and modifications applied directly to the copied file.

Remember that if any future modifications to scaffold.json input file result in a modification the the base stored procedure, these updates will need to be propagated to the copied file as well. Because of this, it is best practice to put an -- Extended comment next to each modification in the copied stored procedure file, to easily identify which parts came from the generated version.

Extending C# Code

Every generated C# class is marked as partial in order to allow extending models and classes with additional properties and methods. Every generated method is also marked as virtual in order to allow overrides. To support overriding methods from custom partial classes, the structure of the generated C# code is such that a partial class extends a base class that contains the actual implementation, which allows overrides of the base class's methods from custom partial extensions.

In cases when a layer requires custom implementation such that extending and/or overriding the base generated code will not suffice, the generation of code at the desired layer can be turned off per entity using various disableGeneration settings.

Try It Out

The easiest way to try out the Scaffold tool is to start with the Plinth API Starter project, which provides the project structure as well as a sample scaffold.json definition.

Clone the repository and follow the instructions in the API starter project to create a new project.

Then, run build-scaffold.bat for the first time to generate code using the sample configuration provided in scaffold.json file.

Make the desired changes to scaffold.json and re-run build-scaffold.bat to update generated code.

Support

While efforts are being made to maintain full support of every combination of the settings mentioned above, the reality is such that settings that are used in the more recent projects are the ones that get the most attention. For example, while PGSQL is a supported option for database code generation, row-level security and bulk insert stored procedures are only currently available in the MSSQL version of the database code, since there are no known current projects that use PGSQL.

If you find a bug or a gap in the settings that are needed for your project, please contact the Scaffold tool's administrators to report a bug, submit a feature request, or ask for assistance.

Alternatively, feel free to fork and modify the code to your liking, and submit a pull request for bug fixes or non-project-specific functionality.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last updated
1.0.25 208 11/7/2024
1.0.24 317 10/11/2024
1.0.22 261 9/18/2024
1.0.21 1,454 2/6/2024
1.0.20 191 2/4/2024
1.0.19 175 1/23/2024
1.0.18 178 1/17/2024
1.0.17 269 1/9/2024
1.0.16 210 1/2/2024
1.0.15 328 12/18/2023
1.0.14 206 12/18/2023
1.0.13 323 12/4/2023
1.0.12 321 7/30/2023
1.0.11 243 7/30/2023
1.0.10 266 5/12/2023
1.0.9 281 3/31/2023
1.0.8 333 3/17/2023
1.0.7 356 2/21/2023
1.0.6 329 2/11/2023
1.0.4 324 2/7/2023
1.0.3 308 2/6/2023
1.0.2 425 1/18/2023
1.0.1 372 1/16/2023
1.0.0 494 8/31/2022
1.0.0-b3.98d44555 126 8/31/2022
1.0.0-b2.84a70868 115 8/31/2022

initial release of Plinth.Scaffold