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.
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.
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.
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.
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.
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
.
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.
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.
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 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.
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+).
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 |
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 |
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
.
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 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.
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.
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. |
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 ). |
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.
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.
See documentation for details on this specific layer.
See documentation for details on this specific layer.
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) . |
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.
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.
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.
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.
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();
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.
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.
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.
The existing FixtureSetUp.OneTimeSetUp
approach has been updated as a result of UnitTestEx
. See FixtureSetUp.cs
for an example implementation.
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:
PersonTest.cs
forBeef.Test.NUnit
implementation.EmployeeTest.cs
for UnitTestEx exclusive implementation.
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 renamedCollectionResult.Items
; as such any references to the preivousResult
property will need to be updated toItems
. - The
ExpectUniqueKey
method has been deprecated; replace with eitherExpectIdentifier
orExpectPrimaryKey
depending on underlying entity implementation. - The
WebApiRequestOptions
class has been deprecated; replace withHttpRequestOptions
. TheIncludeRefDataText
property has been renamed toIncludeText
. - The
WebApiPatchOption
class has been deprecated; replace withHttpPatchOption
. - The
TestSetUp.ConcurrencyErrorETag
has been deprecated; replace withTestSetUp.Default.ConcurrencyErrorETag
. - The
ExpectEvent
contract has changed, withExpectEventValue
,ExpectDestinationEvent
andExpectDestinationEventValue
also added for more advanced scenarios.
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()));
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 withAction<IServiceCollection>
, and as such the invoking code will need to be updated accordingly. - The
AddGeneratedValidationServices()
for theIServiceCollection
has been deprecated; the validators are now added using the newAddValidators<TAssembly>()
. TheAddValidationTextProvider()
will also need to be added to ensure the validation message text provider is registered. - The
AddJsonSerializer
andAddReferenceDataOrchestrator
for theIServiceCollection
will be required to enable JSON serialization and underlying reference data orchestration. - The
ValidationTester.Messages
method has been deprecated and replaced withValidationTester.Errors
which is functionally equivalent. - The
ValidationTester.AddScopedService
method has been deprecated and replaced withValidationTester.MockScoped
which is functionally equivalent.
See EmployeeValidatorTest.cs
for an example implementation.