Overview

Jerrycurl is a free and open source object-relational mapper available via NuGet. It offers a high-level approach to mapping your database and allows for elegant queries and commands written in plain SQL mixed with strongly typed Razor code.

This approach combines the type safety of LINQ-based mappers like Entity Framework Core with the versatility and performance of SQL-based mappers like Dapper and allows you to design robust data access layers in a few easy steps:

1. Install the engine and support for one of our databases.

>
>
dotnet add package Jerrycurl
dotnet add package Jerrycurl.Vendors.SqlServer

2. Generate your database model with our CLI.

>
jerry scaffold -v sqlserver -c "SERVER=.;DATABASE=jerrystore;TRUSTED_CONNECTION=true"

3. Design a lean domain model

1
2
3
4
5
6
7
8
9
10
11
public class CustomerStatsView
{
    public string CustomerName { get; set; }
    public string CustomerEmail { get; set; }
    public decimal NumberOfOrders { get; set; }
}

public class CustomerFilter
{
    public DateTime MinAge { get; set; }
}

4. Compose SQL using expressive Razor syntax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@result CustomerStatsView
@model CustomerFilter
@project Customer c
@project Order o

SELECT
    @c.Col(m => m.Name)         AS @R.Prop(m => m.CustomerName),
    @c.Col(m => m.Email)        AS @R.Prop(m => m.CustomerEmail),
    (
        SELECT  COUNT(*)
        FROM    @o.Tbl()
        WHERE   @o.Col(m => m.CustomerId) = @c.Col(m => m.Id)
    )                           AS @R.Prop(m => m.NumberOfOrders)
FROM
    @c.Tbl()
WHERE
    @c.Col(m => m.Created) >= @M.Par(m => m.MinAge)

5. Expose your queries and commands to the world.

1
2
3
4
5
6
7
8
9
10
11
12
public class CustomersAccessor : Accessor
{
    public async Task<IList<CustomerStatsView>>> GetStats(DateTime minAge)
    {
        var filter = new CustomerFilter()
        {
            MinAge = minAge,
        };

        return await this.QueryAsync<CustomerStatsView>();
    }
}

Performance

Jerrycurl has high performance across the board and excels for graph-based queries. For details about what to expect, consult our pretty charts or view the results from our latest run.

Abstractions

To understand the basic concepts behind the steps above, we'll show diagrams of the components that make up the object-relational mapping process through the MVC and CQS design patterns.

At the lowest level we have a basic ORM that acts as a simple abstraction over the ADO.NET connector specification. This is the component that allows you to send SQL payloads containing statements and parameters over the wire and process the relational result sets into a collection of matching objects.

The engine implements it functionality based on the MVC pattern in a way identical how you build web applications with ASP.NET MVC. This means that if you have experience with ASP.NET, you can apply your existing knowledge about controllers and views to the similar concepts of accessors and procedures.

The CQS pattern further splits procedures into either queries or commands. This means that for every procedure you write with Razor, you should actively choose if you are writing SQL that reads from or writes to the database layer, but never both.

By following these conventions we allow you not only to interact strongly with your database, but also offer up a set of design principles that greatly enhance the way you design and maintain your data access, even for large projects.

Now that we have looked through the architecture, we'll move on to a few examples on how this approach works in a real-world scenario.