Coder Social home page Coder Social logo

rwredding / jerrycurl Goto Github PK

View Code? Open in Web Editor NEW
35.0 2.0 3.0 1.26 MB

Razor-powered ORM for .NET

Home Page: https://jerrycurl.net

License: MIT License

PowerShell 2.04% Shell 0.01% C# 97.72% TSQL 0.09% PLSQL 0.09% PLpgSQL 0.04%
orm razor sql mvc csharp

jerrycurl's Introduction

NuGet Build status Test status Gitter chat

Jerrycurl - MVC and Razor-powered ORM for .NET

Jerrycurl is an object-relational framework that allows developers to build data access layers in a way similar to how web applications are built with ASP.NET MVC.

Installation

Jerrycurl can be installed into any SDK-style C# project from NuGet. The main package contains support for compiling .cssql files into your project and executing them via the built-in MVC engine. Additionally you can install support for one of our supported databases from NuGet as well.

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

Tooling

If you want to generate a ready-to-go object model from your database, install our CLI from NuGet.

> dotnet tool install --global dotnet-jerry

This enables the jerry executable anywhere on your machine. In our case we'll use the scaffold command to generate a .cs file with classes matching a local database about movies.

> jerry scaffold -v sqlserver -c "SERVER=.;DATABASE=moviedb;TRUSTED_CONNECTION=true" -ns "MovieDb.Database"
Connecting to database 'moviedb'...
Generating...
Generated 7 tables and 21 columns in Database.cs.

To learn more about our CLI, type in jerry help.

MVC design

After installing the packages above you can start adding the different components to your project. This should feel familiar to anyone acquainted with ASP.NET MVC, and for the most part Jerrycurl aligns itself with this framework, only with a slightly different terminology.

So where your ASP.NET application contains models, controllers and Razor HTML-based views, Jerrycurl separates your project into models, accessors and procedures written with Razor SQL syntax.

Model layer

The model layer is a collection of POCO-like classes that represent tables and customized datasets for your operations. Each model can be mapped at any depth with any type of data relationship: one-to-one, one-to-many, many-to-one and self-joins.

// Database.cs
[Table("dbo", "Movie")]
class Movie
{
    [Id, Key("PK_Movie")]
    public int Id { get; set; }
    public string Title { get; set; }
    public int Year { get; set; }
}
// Views/Movies/MovieTaglineView.cs
class MovieTaglineView : Movie
{
    public string Tagline { get; set; }
}
// Views/Movies/MovieRolesView.cs
class MovieRolesView : Movie
{
    public IList<MovieRole> Roles { get; set; }
}

Procedure (view) layer

Procedures are written in .cssql files and separated into commands that write data (INSERT, UPDATE, DELETE) and queries that read data (SELECT). Both are written with a combination of SQL and Razor code generating SQL payloads directly from your object model.

-- Queries/Movies/GetMovies.cssql
@result MovieTaglineView
@model MovieFilter
@project MovieDetails d

SELECT     @R.Star(),
           @d.Col(m => m.Tagline) AS @R.Prop(m => m.Tagline)
FROM       @R.Tbl()
LEFT JOIN  @d.Tbl() ON @d.Col(m => m.MovieId) = @R.Col(m => m.Id)
WHERE      @R.Col(m => m.Year) >= @M.Par(m => m.SinceYear)
-- Commands/Movies/AddMovies.cssql
@model Movie

@foreach (var v in this.M.Vals())
{
    INSERT INTO @v.TblName() ( @v.In().ColNames() )
    OUTPUT      @v.Out().Cols("INSERTED").As().Props()
    VALUES                   ( @v.In().Pars() )
}

Accessor (controller) layer

Accessors provide the bridge from your code to the consumer by exposing a collection of methods that executes Razor commands and queries and maps their resulting data sets to matching objects.

// Accessors/MoviesAccessor.cs
public class MoviesAccessor : Accessor
{
    public IList<MovieTaglineView> GetMovies(int sinceYear) // -> Queries/Movies/GetMovies.cssql
        => this.Query<MovieTaglineView>(model: new MovieFilter { SinceYear = sinceYear });
    
    public void AddMovies(IList<Movie> newMovies) // -> Commands/Movies/AddMovies.cssql
        => this.Execute(model: newMovies);
}

Domain (application) layer

Domains provide a central place for fetching configuration for any (or a subset of) your database operations.

// MovieDomain.cs
class MovieDomain : IDomain
{
    public void Configure(DomainOptions options)
    {
        options.UseSqlServer("SERVER=.;DATABASE=moviedb;TRUSTED_CONNECTION=true");
        options.UseJson();
    }
}

Features

  • Official support for SQL Server, PostgreSQL, MySQL, Oracle and SQLite
  • CLI tool to easily generate classes from your database schema
  • Extensive collection of typesafe Razor extensions for all boilerplate SQL
  • Single queries that map complete object graphs of any cardinality
  • Batchable commands through simple @foreach expressions
  • Easy integration with any dependency injection container
  • High performance and support for all operations synchronously or asynchronously
  • Organized, ASP.NET-like project conventions with MVC
  • Native command-query separation suitable for ACID or BASE/CQRS scenarios
  • JSON support through Newtonsoft.Json or System.Text.Json
  • Integration with existing Entity Framework Core models
  • Modern language features with .NET Standard 2.1 and C# 8
  • Free and available via NuGet

To learn more about Jerrycurl and how to get started, visit our official site or check our samples repo.

Building from source

Jerrycurl can be built on any OS supported by .NET Core and included in this repository is a script that performs all build-related tasks.

Prerequisites

  • .NET Core SDK 3.0
  • .NET Core Runtime 2.1+ / 3.0 (to run tests)
  • PowerShell 5.0+ (PowerShell Core on Linux/macOS)
  • Visual Studio 2019 (16.3+) (optional)
  • Docker (optional - for live database testing)

Clone, Build and Test

Clone the repository and run our build script from PowerShell.

PS> git clone https://github.com/rwredding/jerrycurl
PS> cd jerrycurl
PS> .\build.ps1 [-NoTest] [-NoPack]

This runs the Restore, Clean, Build, [Test] and [Pack] targets on jerrycurl.sln and places any packaged .nupkg in the /artifacts/packages folder. Each target can also be run manually in Visual Studio if preferred.

By default, the Test target skips any unit test that requires live running database server. To help you to include these, you can run our docker compose script to boot up instances of our supported databases.

PS> .\test\tools\boot-dbs.ps1 up sqlserver,mysql,postgres,oracle

Please allow ~60 seconds for the databases to be ready after which you can re-run build.ps1; it will then automatically target the included databases instances. When done, you can tear everything down again.

PS> .\test\tools\boot-dbs.ps1 down sqlserver,mysql,postgres,oracle

If you already have an empty database running that can be used for testing, you can manually specify its connection string in the environment variable JERRY_SQLSERVER_CONN, JERRY_MYSQL_CONN, JERRY_POSTGRES_CONN or JERRY_ORACLE_CONN.

Pulling the Oracle Database image requires that you are logged into Docker and have accepted their terms of service.

jerrycurl's People

Contributors

rhodosaur avatar rwredding avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

jerrycurl's Issues

Connection management issue

Seems there is an issue when executing multiple QueryData or CommandData on the same connection with it not reopening/not closing based on its initial state. Needs to handle open/closed connections separately.

Repro:

var options = new QueryOptions()
{
    ConnectionFactory = () => connection // already open/closed/whatever
};
new QueryHandler(options).Handle(query);
// connection should have its initial state
new QueryHandler(options).Handle(query);
// connection should have its initial state again

Not an issue with the MVC engine, as it invokes ConnectionFactory once per operation.

Rearrange data annotations

Data annotations should be moved away from the .Abstractions libraries and IdAttribute should be moved to Jerrycurl.Data to allow CLI scaffolding that works without referencing Jerrycurl.Mvc due to the dependency graph introduced in #4.

Add a few more options to CLI

There needs to be a few more options for the CLI's scaffold command.

Proposal: add --exclude, --include and --type options to include/exclude tables/columns and change types.

Ability to specify nullable key

Currently you cannot decorate a property with [Key] if the value can be null due to the ignore-if-null behavior of mapper. Need a way to define this for keys that are used solely for hash joins, but are possibly null.

Example:

public class CustomerItemsView
{
    public string CustomerName { get; set; }
    [Key] // nullable, since it's possible to not have an order
    internal int? LatestOrderId { get; set; }
    public IList<LatestOrderItem> LatestItems { get; set; }

    public class LatestOrderItem
    {
        [Ref]
        internal int LatestOrderId { get; set; }
        public string Product { get; set; }
    }
}
@result CustomerItemsView

SELECT
    'Semmi'    AS @R.Prop(m => m.CustomerName),
    NULL       AS @R.Prop(m => m.LatestOrderId)

This will skip rows without an order, as it treats LatestOrderId as primary key and hence cannot bind nulls.

Proposed solution: IsPrimary property for KeyAttribute with default true.

Set TransactionScopeAsyncFlowOption for async compatibility

Even though TransactionScope does not provide a CompleteAsync, it should still work with asynchronous operations. Therefore we should set TransactionScopeAsyncFlowOption.Enabled for all
instances of TransactionScopeFilter, or it will throw with a "unknown thread" exception.

Dynamics don't always work out of the box

Currently there's an issue with dynamics if Microsoft.CSharp is not added. Make sure to include this package wherever necessary without introducing too much DLL noise.

Better procedure lookups

  • ProcLocator should look up queries only in /Queries folder and vice versa for commands in /Commands. Currently they do lookups in both (usually no name clashes, but still.)
  • It should implement some form of "suffix"-lookup for partials, so e.g. a NewMessage.cssql file can look up NewMessage.CreateThread.cssql via @M.Subcommand("CreateThread"). This lookup should be the lowest priority.

NuGet modularization

Currently all modules are in the Jerrycurl.Mvc package. Split this into a Jerrycurl.Relations -> Jerrycurl.Data -> Jerrycurl.Mvc dependency graph.

Async IFilter implementation

Currently IFilter and IFilterHandler only expose the synchronous parts of the ADO.NET connector.
Add async targets to utilize for example DbTransaction.CommitAsync and DbTransaction.RollbackAsync.

Align transaction support for all vendors

There are various differences on transactional support between vendors. Some use implicit transactions for single queries/commands, some don't; some support TransactionScope, some don't.

Align and test these various behaviors to align transactional support.

Improve "Procedure not found" exception text

Currently it's something like this:

No conversion found between page type 'System.Collections.Generic.IList<MessageNewView>'
and requested type 'System.Collections.Generic.IList<MessageReplyView>'.

Let try and ditch the IList<T> wrapper when only T differs; this is better:

No conversion found between page type 'MessageNewView' and requested type
'MessageReplyView'.

Better extension points when using DI

Currently the IDomain activator (ProcEngine) is closed off and makes it hard to provide dependencies for domains (a normal scenario for reading connection strings.)

private IDomain CreateDomain(Type domainType)
{
    try
    {
        return (IDomain)Activator.CreateInstance(domainType);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("Unable to create domain.", ex);
    }
}

Proposed: add an optional IServiceProvider argument which - if supplied - uses IServiceProvider.GetService to instantiate domains:

private IDomain CreateDomain(Type domainType)
{
    try
    {
        return (IDomain)(this.serviceProvider?.GetService(domainType) ?? Activator.CreateInstance(domainType));
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("Unable to create domain.", ex);
    }
}

[Json] for interfaces?

Currently it can only be defined on classes/structs/properties. Check how the metadata builder reads JsonAttribute through inheritance and if it makes sense to add it to an interface.

Deprecate iconUrl in .nuspec

Favor the new iconproperty that includes the icon in the actual nupkg and remove NU5048 from the nowarn list.

CLI runs should respect local NuGet config

As CLI intermediate builds are placed in the .dotnet folder, they don't respect the local nuget.config hierarchy from the caller's working directory. We need a way to duplicate this behavior between the two folders.

It helps with local builds when testing preview CLI packages.

$PublicRelease switch overrides AppVeyor defaults

Currently public releases are build by AppVeyor with commit suffix, since we're always supplying the $PublicRelease argument, there by overriding the publicReleaseRefSpec regex from version.json.

Make the argument optional based on its value.

Downtarget SQL Server libraries

Currently we are targeting System.Data.SqlClient v4.7.0 which is a bit high for some scenarios, and lower versions should work just as fine (with the possibility of a manual upgrade, if needed.)

Also there's the new Microsoft.Data.SqlClient package which we should also offer up NuGet targets for.

Proposed: retarget System.Data.SqlClient to v4.6.* and add a new NuGet for using the new Microsoft.Data.SqlClient.

Enable MySQL on CI builds

Check if there are still issues with MySQL on the AppVeyor VS19 image and if so downgrade to VS17 and install .NET Core SDK 3.0 manually with Chocolatey similar to how Dapper does it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.