IFYates / Phorm

Procedure-heavy object-relational mapping framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Pho/rm - The Procedure-heavy object-relational mapping framework

Build & Test Code Coverage Mutation testing badge Code Quality

A full O/RM, focused on strong separation between the data structures and the business entity representation.

See our ethos for how and why Pho/rm is different to other O/RMs.

The wiki contains lots of useful examples of the various features, as well as a getting started guide.

Pho/rm supports:

Packages
IFY.Phorm.Core NuGet Version NuGet Downloads
IFY.Phorm.SqlClient NuGet Version NuGet Downloads

Driving principals

The are many, brilliant O/RM frameworks available using different paradigms for database interaction.
Many of these allow for rapid adoption by strongly-coupling to the storage schema at the expense of control over the efficiency of the query and future structural mutability.
As such solutions grow, it can become quickly difficult to evolve the underlying structures as well as to improve the way the data is accessed and managed.

Pho/rm was designed to provide a small and controlled surface between the business logic layer and the data layer by pushing the shaping of data to the data provider and encouraging the use of discrete contracts.
Our goal is to have a strongly-typed data surface and allow for a mutable physical data structure, where responsibility of the layers can be strictly segregated.

With this approach, the data management team can provide access contracts to meet the business logic requirements, which the implementing team can rely on without concern over the underlying structures and query efficiency.

flowchart RL
subgraph Database
    D[(Data)]
    V((vw))
    SP((sp))
end
subgraph Application
    O[DTO]
    I[/Interface/]
end

D -->|Get| O;
D --> V -->|Get| O;
SP -->|From.Get| O;
O -.->|Call/From| I --> SP --> D;
Loading

Common example

For typical entity CRUD support, a Pho/rm solution would require a minimum of:

  1. Existing tables in the data source
  2. A POCO to represent the entity (DTO); ideally with a contract for each database action
  3. A stored procedure to fetch the entity
  4. At least one stored procedure to handle create, update, delete (though, ideally, one for each)

A simple Pho/rm use would have the structure:

CREATE TABLE [dbo].[Data] (
    [Id] BIGINT NOT NULL PRIMARY KEY,
    [Key] NVARCHAR(50) NOT NULL UNIQUE,
    [Value] NVARCHAR(256) NULL
)

CREATE PROCEDURE [dbo].[usp_SaveData] (
    @Key NVARCHAR(50),
    @Value NVARCHAR(256),
    @Id BIGINT = NULL OUTPUT
) AS
    SET NOCOUNT ON
    INSERT INTO [dbo].[Data] ([Key], [Value])
        SELECT @Key, @Value
    SET @Id = SCOPE_IDENTITY()
RETURN 1 -- Success
// DTO and contracts
[PhormContract(Name = "Data")] // Name of underlying table (optional)
class DataItem : ISaveData
{
    public long Id { get; set; }
    public string Key { get; set; } = string.Empty;
    public string? Value { get; set; }
}
interface ISaveData : IPhormContract
{
    long Id { set; } // Output
    string Key { get; }
    string? Value { get; }
}

// Configure Pho/rm session to SQL Server
IPhormSession session = new SqlPhormSession(connectionString);

// Get all existing records from the table
DataItem[] allData = session.Get<DataItem[]>()!; // Table dbo.Data

// Add a new record to the table, getting back the new id
var newItem = new { Id = ContractMember.Out<long>(), Key = "Name", Value = "T Ester" };
int result = session.Call<ISaveData>(newItem); // Procedure dbo.usp_SaveData

DataItem? itemById = session.Get<DataItem>(new { Id = newItem.Id }); // Table dbo.Data
DataItem? itemByKey = session.Get<DataItem>(new { Key = "Name" }); // Table dbo.Data

Syntax overview

IPhormSession
    // Calling a contract
    int Call(string contractName, object? args = null);
    int Call<TActionContract>(object? args = null);
    Task<int> CallAsync(string contractName, object? args = null, CancellationToken cancellationToken = CancellationToken.None);
    Task<int> CallAsync<TActionContract>(object? args = null, CancellationToken cancellationToken = CancellationToken.None);

    // Fetching from a DTO definition (table, view)
    TResult? Get<TResult>(object? args = null);
    Task<TResult?> GetAsync<TResult>(object? args = null, CancellationToken cancellationToken = CancellationToken.None);

    // Fetching from a named procedure
    From(string contractName, object? args = null)
        TResult? Get<TResult>();
        Task<TResult?> GetAsync<TResult>(CancellationToken cancellationToken = CancellationToken.None);
        // Resultset filtering
        Where<TEntity>(Expression<Func<TEntity, bool>> predicate)
            IEnumerable<TEntity> GetAll();
            Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = CancellationToken.None);
    
    // Fetching from a contract definition (procedure, table, view)
    From<TActionContract>(object? args = null)
        TResult? Get<TResult>();
        Task<TResult?> GetAsync<TResult>(CancellationToken cancellationToken = CancellationToken.None);
        // Resultset filtering
        Where<TEntity>(Expression<Func<TEntity, bool>> predicate)
            IEnumerable<TEntity> GetAll();
            Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = CancellationToken.None);

About

Procedure-heavy object-relational mapping framework

License:Mozilla Public License 2.0


Languages

Language:C# 100.0%