Skip to content

Latest commit

 

History

History
103 lines (77 loc) · 6.73 KB

Layer-Data.md

File metadata and controls

103 lines (77 loc) · 6.73 KB

Data (Data access)

The Data layer is primarily responsible for performing the persistence CRUD (Create, Read, Update or Delete) and Query requests, handling the underlying wire transport, protocols and payload to enable.

This is the area in which a developer using Beef is likely to spend most of their time. The basic CRUD capabilities are enabled mostly out-of-the-box; however, the data access logic complexity will likely exceed this as the complexity of the requirements increase.

There are Beef capabilities to encapsulate the data access in a largely consistent manner; in that they largely support the following pattern (where Xxx is the data access name):

Class Description
XxxBase Provides the base data access logic that is inherited to enable. This encapsulates access to the standard Get, Create, Update, Delete and Query operations.
XxxArgs Provides a consistent means to pass data source specific arguments to the underlying operations.
XxxQuery Encapsulates the query logic supporting SelectFirst, SelectFirstOrDefault, SelectSingle, SelectSingleOrDefault and SelectQuery (selects multiple items and either creates, or updates an existing, collection; and where the corresponding Paging is provided the configured paging, and optional get count, will be enacted.)
XxxMapper Provides mapping of a .NET entity to/from the data source representation (where applicable). This allows for property name changes, type conversion, and sub-entity mapping.

Supported

The following data access capabilities are currently supported by a corresponding Beef framework; as well as being integrated into the code-generation:

Assembly Description
Beef.Data.Database ADO.NET database framework.
Beef.Data.EntityFrameworkCore Entity Framework (EF) Core framework.
Beef.Data.Cosmos Cosmos DB execution framework.
Beef.Data.OData OData execution framework.

This obviously does not prohibit access to other data sources; just that these will need to be implemented in a custom manner.


Usage

This layer is generally code-generated and provides options to provide a fully custom implementation, and has extension opportunities to inject additional logic into the processing pipeline.

The Operation element within the entity.xml configuration primarily drives the output. There is a generated class per Entity named {Entity}Data.

There is also a corresonding interface named I{Entity}Data generated so the likes of test mocking etc. can be employed.


Code-generated

An end-to-end code-generated processing pipeline generally consists of:

Step Description
DataInvoker The logic is wrapped by a DataInvoker. This enables the BusinessInvokerArgs options to be specified, including DataContextScopeOption, TransactionScopeOption and Exception handler. These values are generally specified in the code-generation configuration.
XxxArgs The creation of the data source specific arguments (for example, a stored procedure name for a relational database).
OnBefore The OnBefore extension opportunity; where set this will be invoked. This enables logic to be invoked before the primary Operation is performed.
Operation The actual operation execution (Get, Create, Update, Delete and Query). This will include an OnQuery extension opportunity where performing a Query.
OnAfter The OnAfter extension opportunity; where set this will be invoked. This enables logic to be invoked after the primary Operation is performed.

The following demonstrates the usage (a snippet from the sample PersonData):

/// <summary>
/// Gets the <see cref="Person"/> collection object that matches the selection criteria.
/// </summary>
/// <param name="args">The Args (see <see cref="PersonArgs"/>).</param>
/// <param name="paging">The <see cref="PagingArgs"/>.</param>
/// <returns>A <see cref="PersonCollectionResult"/>.</returns>
public Task<PersonCollectionResult> GetByArgsAsync(PersonArgs args, PagingArgs paging)
{
    return DataInvoker.Default.InvokeAsync(this, async () =>
    {
        PersonCollectionResult __result = new PersonCollectionResult(paging);
        var __dataArgs = DbMapper.Default.CreateArgs("[Demo].[spPersonGetByArgs]", __result.Paging);
        if (_getByArgsOnBeforeAsync != null) await _getByArgsOnBeforeAsync(args, __dataArgs);
        __result.Result = Database.Default.Query(__dataArgs, p => _getByArgsOnQuery?.Invoke(p, args, __dataArgs)).SelectQuery<PersonCollection>();
        if (_getByArgsOnAfterAsync != null) await _getByArgsOnAfterAsync(__result, args);
        return __result;
    }, new BusinessInvokerArgs { ExceptionHandler = _getByArgsOnException });
}

Custom

A custom (OnImplementation) processing pipeline generally consists of:

Step Description
DataInvoker The logic is wrapped by a DataInvoker. This enables the BusinessInvokerArgs options to be specified, including DataContextScopeOption, TransactionScopeOption and Exception handler. These values are generally specified in the code-generation configuration.
OnImplementation Invocation of a named XxxxxOnImplementaionAsync method that must be implemented in a non-generated partial class.

The following demonstrates the usage (a snippet from the sample PersonData):

/// <summary>
/// Gets the <see cref="PersonDetail"/> collection object that matches the selection criteria.
/// </summary>
/// <param name="args">The Args (see <see cref="PersonArgs"/>).</param>
/// <param name="paging">The <see cref="PagingArgs"/>.</param>
/// <returns>A <see cref="PersonDetailCollectionResult"/>.</returns>
public Task<PersonDetailCollectionResult> GetDetailByArgsAsync(PersonArgs args, PagingArgs paging)
{
    return DataInvoker.Default.InvokeAsync(this, () => GetDetailByArgsOnImplementationAsync(args, paging),
        new BusinessInvokerArgs { ExceptionHandler = _getDetailByArgsOnException });
}