Skip to content

Latest commit

 

History

History
470 lines (297 loc) · 28.9 KB

Upgrade-v4-to-v5.md

File metadata and controls

470 lines (297 loc) · 28.9 KB

Upgrade Beef version 4.x to 5.x

Beef, as of version 5.x, is ostensibly the code-generation engine, and solution orchestration, that ultimately takes dependencies on the following capabilities to enable the end-to-functionality and testing thereof in a standardized (albiet somewhat opinionated) manner:

  • CoreEx - provides the core runtime capabilties (extends .NET core);
  • UnitTestEx - provides extended unit and intra-domain integration testing;
  • DbEx - provides extended database management capabilties;
  • OnRamp - provides the underlying code-generation engine functionality.

Prior to version 5.x, Beef was all encompassing. These capabilities have been extracted, simplified and refactored to be first class frameworks in their own right, and made into the repos listed above. This allows them to be used and maintained independently to Beef; therefore, offering greater opportunities for reuse versus all-or-nothing.


Breaking changes

Version 5.x is a major refactoring (improvement and simplification) with respect to the underlying runtime primarily, and although effort was made to minimize impacts on upgrading from version 4.x, this was unfortunately unavoidable.


YAML-only code-generation configuration

The XML-based code-generation configuration has been deprecated; the XML will have to be converted to YAML before attempting to upgrade. The 4.2.x code-generation console supports -x2y|--xml-to-yaml to perform this action.


Autonomous entity scope

In version 4.2.x an entityScope property was added to the entity code-generation configuration. Where this was not explicitly defined it would have defaulted to Common, that indicated that the rich entities (inheriting from EntityBase) were to be generated in the Common project. Alternatively, a value of Business would indicate that the rich entities were to be generated in the Business project.

A further Autonomous option was available that generated two contractually-identical entities, the rich entities (inheriting from EntityBase) generated in the Business project, and corresponding basic entities (no EntityBase inheritance) generated in the Common project. This was intended to simplify usage, and remove dependencies related to the rich Reference Data, within Common where leveraging the Agent to invoke the APIs.

The entityScope property has been deprecated in 5.x as Autonomous is now the one-and-only behavior. Additional code-generation properties such as internalOnly and omitEntityBase further drive how the entity-based artefacts are generated.


Railway-oriented programming

CoreEx version 3.0.0 introduced monadic error-handling, often referred to as Railway-oriented programming. This is enabled via the key types of Result and Result<T>; please review the corresponding documentation for more detail on purpose and usage.

The Result and Result<T> have been integrated into the code-generated output and is leveraged within the underlying validation. This is intended to simplify success and failure tracking, avoiding the need, and performance cost, in throwing resulting exceptions.

Also, note that this may introduce some breaking changes that will need manual remediation. The validation OnValidateAsync and CustomRule must now return a Result. Where using, consider leveraging the Result.XxxError to return known errors versus throwing the related exception.

This is implemented by default; however, can be disabled by setting the useResult attribute to false within the code-generation configuration.


JSON serialization

Previously, Beef leveraged Newtonsoft exclusively for JSON serialization. CoreEx is JSON serializer implementation agnostic; however, System.Text.Json is considered the preferred (default).

From a code-generation perspective a new YAML jsonSerializer property can be used to specify preferred; defaults to SystemText; otherwise, specify Newtonsoft.


Reference data

There has been a significant refactoring of the existing ReferenceDataManager; this functionality is now enabled by the CoreEx ReferenceDataOrchestrator. This new class now fully encapsulates the reference data access, caching, and loading. The v5.x reference data code generation has been updated accordingly.


Object-property mapping

Where previously leveraging the likes of Entity Framework (EF) object-property mapping between types was managed leveraging AutoMapper to enable. CoreEx provides a flexible implementation agnostic approach to enable mapping; however, for Beef the code-generation exclusively leverages the new simpler in-built Mapper functionality.


Async cancellation token

All CoreEx Async methods include a CancellationToken as the final parameter to support the recommended .NET asynchronous programming pattern.

Where overridding previous Async-related methods these will need to be updated to include this new parameter. Generally, where invoking the method the CancellationToken will be optional; i.e. will default where not specified.


Cleaning

Cleaning is where the underlying value for a parameter within the Manager-layer is adjusted based on Type and underlying configuration; specifically where ICleanUp is leveraged. This is now only performed where explicitly configured; within CodeGeneration, Entity(s) and/or Operation(s) YAML. Cleaning is a feature that is generally infrequently used and is best excluded unless needed.


Check class

Beef previously contained a Check class that was used to check method parameters and throw a corresponding ArgumentNullException or ArgumentException; this class does not exist in CoreEx. The code that previously leveraged will need to be refactored, or the developer will need to re-implement the Check class.

It is recommended that the developer consider using ArgumentNullException.ThrowIfNull and ArgumentException.ThrowIfNullOrEmpty (.NET 6+).


Package mapping

Following represents the high-level mapping between the existing Beef packages and the corresponding new (where applicable). Not all packages are functionally equivalent, some capabilities may have been deprecated.

Existing New
Beef.Abstractions CoreEx
Beef.AspNetCore.WebApi CoreEx.AspNetCore
Beef.Core CoreEx, CoreEx.Validation, CoreEx.Newtonsoft, CoreEx.AutoMapper
Beef.Data.Database CoreEx.Database.SqlServer includes CoreEx.Database
Beef.Data.Database.Cdc CoreEx.Database.SqlServer
Beef.Data.EntityFrameworkCore CoreEx.EntityFrameworkCore
Beef.Data.Cosmos CoreEx.Cosmos
Beef.Data.OData None (on roadmap)
Beef.Events CoreEx (CoreEx.Events namespace)
Beef.Events.EventHubs None (on roadmap)
Beef.Events.ServiceBus CoreEx.Azure (publishing only)
Beef.Grpc None (consider Dapr sidecar)
- -
Beef.CodeGen.Core Beef.CodeGen.Core upgraded (leverages OnRamp)
Beef.Database.Core Beef.Database.SqlServer includes Beef.Database.Core (leverages DbEx)
Beef.Test.NUnit UnitTestEx, Beef.Test.NUnit (backwards compatibility only)
Beef.Template.Solution Beef.Template.Solution

Upgrade guidance approach

This documentation was developed by upgrading the My.Hr solution and recording the steps. This attempts to capture the how, and sometimes the why, logically project-by-project.

Section Project
CodeGen My.Hr.CodeGen
Database My.Hr.Database
Common My.Hr.Common
Business My.Hr.Business
Api My.Hr.Api
Test My.Hr.Test

CodeGen

The code-generation capabilities continue to leverage OnRamp to provide the code-generation scripting, templating and orchestration. All underlying templates have been updated where applicable to output code supporting the Beef 5.x changes. Update the Beef.CodeGen.Core package dependency to the latest 5.x version. The underlying Program.cs logic should require no further changes and should compile successfully.

The *.beef.yaml configuration files must be renamed to *.beef-5.yaml. This is to support new features and ensure the underlying schema validation (intellisense) is targeting version 5.x.

Within CoreEx the existing IUniqueKey interface has been replaced with the IPrimaryKey, as such all references to the code-generation uniqueKey property must be renamed to primaryKey.


Clean

A new Clean command has been added to the code-generator. Where executed the code-generator will recursively discover all Generated folders and will delete all files within (including generated Database artefacts where applicable). This will ensure a clean baseline as not all previously generated artefacts will be generated in the new version.

From the command-line execute the clean command.

dotnet run clean

Re-generate

Re-generate all the existing artefacts leveraging the All command.

dotnet run all

Error messages similar to the following may appear and will need to be corrected for the code-generation to complete successfully.

Config '.\My.Hr\My.Hr.CodeGen\entity.beef-5.yaml' is invalid: [Entity(Name='EmployeeBase').Property(Name='Id').uniqueKey] The 'uniqueKey' configuration has been renamed to 'primaryKey'; please update the configuration accordingly.

Warning messages similar to the following may appear to indicate where previous configuration has been deprecated, etc. Generally, it is advisable to correct the configuration to remove all the warnings.

Warning: Config [entityScope] has been deprecated and will be ignored.
Warning: Config [Entity(Name='EmployeeBase').iValidator] has been deprecated and will be ignored.

Database

The database capabilities have been extended within DbEx to support multiple relational database providers, as such Beef.Database.SqlServer now encapsulates the SQL Server database migration logic. As such, remove all previous Beef.* package dependencies and add the latest Beef.Database.SqlServer package version.

The underlying Program.cs logic has been updated such that a new ConfigureMigrationArgs method has been exposed that the My.Hr.Test can invoke to minimize duplication of any MigrationArgs configuration.

public class Program
{
    /// <summary>
    /// Main startup.
    /// </summary>
    /// <param name="args">The startup arguments.</param>
    /// <returns>The status code whereby zero indicates success.</returns>
    static Task<int> Main(string[] args) => SqlServerMigrationConsole
        .Create("Data Source =.; Initial Catalog = My.Hr; Integrated Security = True; TrustServerCertificate = true", "My", "Hr")
        .Configure(c => ConfigureMigrationArgs(c.Args))
        .RunAsync(args);

    /// <summary>
    /// Configure the <see cref="MigrationArgs"/>.
    /// </summary>
    /// <param name="args">The <see cref="MigrationArgs"/>.</param>
    /// <returns>The <see cref="MigrationArgs"/>.</returns>
    public static MigrationArgs ConfigureMigrationArgs(MigrationArgs args) => args.AddAssembly<Program>().UseBeefSchema();
}

The database.beef.yaml configuration file must also be renamed to database.beef-5.yaml. This is to support new features and ensure the underlying schema validation (intellisense) is targeting version 5.x.

Finally, recompile and execute. There may have been some minor changes to the generated output from the previous version.

dotnet run all

Warning messages similar to the following may appear to indicate where previous configuration has been deprecated, etc. Generally, it is advisable to correct the configuration to remove warnings.

Warning: Config [entityScope] has been deprecated and will be ignored.

Common

Generally remove/update the package dependencies as follows; check any other dependencies to ensure need and update version accordingly.

Remove Instructions
Beef.* Add the latest CoreEx package.
Newtonsoft.Json By default System.Text.Json is used and is included within CoreEx; where Newtonsoft is still required add the CoreEx.Newtonsoft package.

This project should require no further changes and compile successfully.

The generated Common project output has been changed as follows.

Folder Leverages Additional information
Entities CoreEx.Entities See documentation.
Agents CoreEx.Http See documentation. Note that the existing IXxxWebApiAgentArgs and XxxWebApiAgentArgs have been deprecated.

Business

Generally remove/update the package dependencies as follows; check any other dependencies to ensure need and update version accordingly.

Remove Instructions
Beef.Core Add the latest CoreEx and CoreEx.Validation packages.
Beef.Data.Database Add the latest CoreEx.Database.SqlServer package.
Beef.Data.EntityFrameworkCore Add the latest CoreEx.EntityFrameworkCore package.
Beef.* Other Beef dependencies; see package mapping.
Newtonsoft.Json By default System.Text.Json is used and is included within CoreEx; where Newtonsoft is still required add the CoreEx.Newtonsoft package (includes Newtonsoft.Json).

Namespaces

C# 10 as part of .NET 6 introduced the concept of global and implicit usings. This feature is leveraged in the code generation, in that the namespace using statements are no longer output.

To leverage update the .NET project configuration as follows:

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>

A file similar to this GlobalUsings must be added to the project, with adjustments within to the Company.AppName. namespaces specific to the solution being upgraded.

Any custom, non-generated, classes can remove duplicate using statements to simplify code if desired. For the most part, all existing using statements can be removed, and the ImplictUsings file updated to add any additional as required.


Configuration settings

The CoreEx.Configuration namespace provides the new SettingsBase class that provides a flexible, centralized, means to manage IConfiguration.

A new settings class, similar to HrSettings, must be created. The underlying SettingsBase contains a number of pre-configured settings that are leveraged by CoreEx at runtime. Any additional settings, for example connection strings, should be moved to this class where applicable.


Manager-layer logic

See documentation for details on this specific layer.


DataSvcs-layer

See documentation for details on this specific layer.


Data-layer logic

See documentation for details on this specific layer. The following are some of the key changes to the data-layer logic that may be encounted.

Existing Change required
XxxDb The existing custom database class that previously inherited from DatabaseBase must be updated to inherit from SqlServerDatabase. The underlying implementation has changed; see sample HrDb as a guide.
XxxEfDb Where also leveraging Entity Framework (EF) the equivalent HrEfDb and HrEfDbContext will need amending. The Microsoft.EntityFrameworkCore.SqlServer package dependency will also need to be added explicitly.
DatabaseArgs Previously, this class was instantiated within the code-generated partial class, then passed into the corresponding customized partial class. This previously had limited usage and as such has been moved as a property into the owning Database class. Therefore, this parameter will need to be removed from the customized non-generated partial class.
StoredProcedure The StoredProcedure property is no longer included within the DatabaseArgs, it is now the primary method (explicitly specified) to be able to perform an operation on the database; for example _db.StoredProcedure(storedProcedureName).Xxx.
GetParamName This has been renamed to GetParameterName. A developer could choose to implement this as an extension method to enable, this would then minimize changes to existing code where applicable.
SelectQueryMultiSetAsync This has been renamed to SelectMultiSetAsync. A developer could choose to implement this as an extension method to enable, this would then minimize changes to existing code where applicable.
CreateTableValuedParameter This now requires the database to be passed as the first paramater; for example CreateTableValuedParameter(_db, collection).

Validation logic

The CoreEx.Validation is essentially a port of the existing Beef.Validation implementation. There have been some minor changes/rationalizations; however, for the most part this should be largely identical in feature and underlying API.

Where inheriting from Validator<TEntity> the OnValidateAsync(ValidationContext<Employee> context) method signature has been changed to OnValidateAsync(ValidationContext<Employee> context, CancellationToken cancellation) and will be need to be updated accordingly.


API

See documentation for details on this specific layer.

Remove Beef.* packages and replace with CoreEx package (will be automatically included given reference to Business project); check any other dependencies to ensure need and update version accordingly.


Namespaces

C# 10 as part of .NET 6 introduced the concept of global and implicit usings. This feature is leveraged in the code generation, in that the namespace using statements are no longer output.

To leverage update the .NET project configuration as follows:

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>

A file similar to this GlobalUsings must be added to the project, with adjustments within to the Company.AppName. namespaces specific to the solution being upgraded.


Dependency injection

There have been significant changes related to CoreEx and the requirements for Dependency Injection (DI); see documentation for further details.

The event publishing/sending approach has seen sigificant change within CoreEx and will need to be refactored accordingly. Where leveraging a cloud native Microsoft Azure messaging capability review CoreEx.Azure to determine whether a solution exists; if not, this could be an awesome opportunity to contribute.

See Startup.cs for an example implementation.


Application builder configuration

The underlying ASP.NET IApplicationBuilder logic should largely remain unchanged.

The existing UseWebApiExceptionHandler no longer supports parameters; these are now loaded internally leveraging Dependency Injection; see underlying WebApiExceptionHandlerMiddleware.

After the existing UseExecutionContext, an additional UseReferenceDataOrchestrator must be added for the ReferenceDataOrchestrator to function correctly. Example code is as follows.

app.UseExecutionContext();
app.UseReferenceDataOrchestrator();

Program host

Within the existing Program class there was a reference to an existing Beef WebApiStartup class; this has been deprecated. During the port to CoreEx it was decided that the set up should be explicitly managed by the developer, and that Beef should not impose any particular approach, etc. including the usage of embedded configuration files.

The embedded resource usage was needed to support the existing unit testing capabilities; this is no longer required as UnitTestEx has improved functionality to leverage the API as-is without any specific constraints. It is recommended that the developer review the existing embedded configuration file usage and amend/remove where applicable.

See Program.cs for an example implementation.


Test

The Beef v5.x testing has now been replaced by the functionality available within UnitTestEx. Any functionality available within Beef.Test.NUnit is intended to assist with the upgrading from Beef v4.x; contains a subset of the previous functionality. This assembly will likely be deprecated at the next major version.

To use update the Beef.Test.NUnit package dependency to the latest 5.x version.


Namespaces

C# 10 as part of .NET 6 introduced the concept of global and implicit usings. To leverage update the .NET project configuration as follows:

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>

A file similar to this GlobalUsings must be added to the project, with adjustments within to the Company.AppName. namespaces specific to the solution being upgraded.

Note: The Entities namespaces should not be included with the GlobalUsings; these should be specified per file otherwise an ambiguious name reference will occur.

Any custom, non-generated, classes can remove duplicate using statements to simplify code if desired. For the most part, all existing using statements can be removed, and the ImplictUsings file updated to add any additional as required.


Fixture one-time set-up

The existing FixtureSetUp.OneTimeSetUp approach has been updated as a result of UnitTestEx. See FixtureSetUp.cs for an example implementation.


API testing

The existing TestSetUpAttribute and AgentTester have been deprecated during the port to UnitTestEx; however, basic proxies have been created for these within the Beef.Test.NUnit package. To enable add using Beef.Test.NUnit; or, alternatively update to test exclusively using UnitTestEx.

See following examples:

Other challenges may include:

  • Make sure the using Company.AppName.Common.Entities; is declared; otherwise, the entity will not match that returned by the executing agent and an obscure compile error will occur. Note that the common entities do not contain any transformation logic within, so if this was previously assumed then this will need to be manually accounted for; i.e. DateTimeTransform.DateOnly.
  • The existing CollectionResult.Result has been renamed CollectionResult.Items; as such any references to the preivous Result property will need to be updated to Items.
  • The ExpectUniqueKey method has been deprecated; replace with either ExpectIdentifier or ExpectPrimaryKey depending on underlying entity implementation.
  • The WebApiRequestOptions class has been deprecated; replace with HttpRequestOptions. The IncludeRefDataText property has been renamed to IncludeText.
  • The WebApiPatchOption class has been deprecated; replace with HttpPatchOption.
  • The TestSetUp.ConcurrencyErrorETag has been deprecated; replace with TestSetUp.Default.ConcurrencyErrorETag.
  • The ExpectEvent contract has changed, with ExpectEventValue, ExpectDestinationEvent and ExpectDestinationEventValue also added for more advanced scenarios.

Reference data API testing

For tests that return IReferenceData results the reference data-specifc serializer (see ReferenceDataContentJsonSerializer) must be used to effectively deserialize. This must be set before the test executes.

See ReferenceDataTest.cs for an example implementation.

Where using the Beef.Test.NUnit package see follows.

// Existing
using var agentTester = AgentTester.CreateWaf<Startup>();

// New
using var agentTester = AgentTester.CreateWaf<Startup>(configureTester: t => t.UseJsonSerializer(new CoreEx.Text.Json.ReferenceDataContentJsonSerializer()));

Validation testing

The existing ValidationTester.Test with corresponding CreateAndRunAsync pattern has been deprecated within UnitTestEx. The ValidationTester must be explicitly created using the Create method and finally disposed as it implements IDisposable.

// Existing
await ValidationTester.Test()
    .Xxx()
    .CreateAndRunAsync<IValidator<Xxx>, Xxx>(value);

// New
using var test = ValidationTester.Create();

await test.Xxx()
    .RunAsync<IValidator<Xxx>, Xxx>(value);

Other challenges may include:

  • Where ConfigureServices is leveraged this now only supports a parameter with Action<IServiceCollection>, and as such the invoking code will need to be updated accordingly.
  • The AddGeneratedValidationServices() for the IServiceCollection has been deprecated; the validators are now added using the new AddValidators<TAssembly>(). The AddValidationTextProvider() will also need to be added to ensure the validation message text provider is registered.
  • The AddJsonSerializer and AddReferenceDataOrchestrator for the IServiceCollection will be required to enable JSON serialization and underlying reference data orchestration.
  • The ValidationTester.Messages method has been deprecated and replaced with ValidationTester.Errors which is functionally equivalent.
  • The ValidationTester.AddScopedService method has been deprecated and replaced with ValidationTester.MockScoped which is functionally equivalent.

See EmployeeValidatorTest.cs for an example implementation.