AutoBackend.SDK 0.1.5

There is a newer version of this package available.
See the version list below for details.
dotnet add package AutoBackend.SDK --version 0.1.5
NuGet\Install-Package AutoBackend.SDK -Version 0.1.5
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AutoBackend.SDK" Version="0.1.5" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AutoBackend.SDK --version 0.1.5
#r "nuget: AutoBackend.SDK, 0.1.5"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AutoBackend.SDK as a Cake Addin
#addin nuget:?package=AutoBackend.SDK&version=0.1.5

// Install AutoBackend.SDK as a Cake Tool
#tool nuget:?package=AutoBackend.SDK&version=0.1.5

AutoBackend

DEVELOPER BUILDS RELEASE BUILDS
MyGet MyGet NuGet

This package provides the boilerplate infrastructure to create simplified backend services by managing DataBase & API layers. It is a pet project without a commercial base and any promises about further development. If you would like to use this package or sources of this package, please let me know by texting me@vorobalek.dev. I would prefer if any of your scenarios of using this package left consequences in the form of contribution to this repository.

Roadmap?

Honestly, I do not have enough time to do something about that. I wrote this code while looking for a job just for fun, but now I have only decided to publish it. Probably, somewhere I will improve it or maybe even add some more features like GraphQL. I dunno. I have already spent much more time trying to express how it worked than I was supposed to.

Features

Examples

The basic using scenario can be found in the Api project. Also, here is a copy of that samples.

Initialize AutoBackend from your Program.cs file

await new AutoBackend.Sdk.AutoBackendHost<Program>().RunAsync(args);

Create models you need to describe your domain relations

public class Note
{
    public string Content { get; set; } = null!;
}

public class Album
{
    public Guid Id { get; set; }

    public string Title { get; set; } = null!;

    public string? Artist { get; set; }

    public int Score { get; set; } = 0;
}

public class Song
{
    public Guid Id { get; set; }

    public string Title { get; set; } = null!;

    public string? Author { get; set; }

    public string? Text { get; set; }

    public decimal Price { get; set; }
}

public class AlbumContent
{
    [ForeignKey(nameof(Album))]
    public Guid AlbumId { get; set; }

    [ForeignKey(nameof(Song))]
    public Guid SongId { get; set; }

    [DeleteBehavior(DeleteBehavior.Cascade)]
    public Album Album { get; set; } = null!;

    [DeleteBehavior(DeleteBehavior.Cascade)]
    public Song Song { get; set; } = null!;
}

public class AlbumSet
{
    [ForeignKey(nameof(Album1))]
    public Guid Album1Id { get; set; }

    [ForeignKey(nameof(Album2))]
    public Guid? Album2Id { get; set; }

    [ForeignKey(nameof(Album3))]
    public Guid? Album3Id { get; set; }

    [ForeignKey(nameof(Album4))]
    public Guid Album4Id { get; set; }

    [ForeignKey(nameof(Album5))]
    public Guid? Album5Id { get; set; }

    [ForeignKey(nameof(Album6))]
    public Guid? Album6Id { get; set; }

    [ForeignKey(nameof(Album7))]
    public Guid? Album7Id { get; set; }

    [ForeignKey(nameof(Album8))]
    public Guid? Album8Id { get; set; }

    [DeleteBehavior(DeleteBehavior.Cascade)]
    public Album Album1 { get; set; } = null!;

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Album? Album2 { get; set; }

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Song? Album3 { get; set; }

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Song? Album4 { get; set; }

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Song? Album5 { get; set; }

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Song? Album6 { get; set; }

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Song? Album7 { get; set; }

    [DeleteBehavior(DeleteBehavior.SetNull)]
    public Song? Album8 { get; set; }

    public string Name { get; set; } = null!;
}

Mark models which AutoBackend has to generate tables in the database for

  • Use [GenericEntity] to mark the model as a keyless entity.
[GenericEntity]
public class Note
{
    // ...
}
  • Use [GenericEntity(<primary key property name>)] to mark the model as an entity with the primary key displayed as a single property.
[GenericEntity(
    nameof(Id)
)]
public class Album
{
    public Guid Id { get; set; }

    // ...
}

[GenericEntity(
    nameof(Id)
)]
public class Song
{
    public Guid Id { get; set; }

    // ...
}

Use [GenericEntity(<first complex primary key property name>, <second complex primary key property name>, ..., <N-th complex primary key>)] to mark the model as an entity with the primary key displayed as a complex set of properties.

[GenericEntity(
    nameof(AlbumId),
    nameof(SongId)
)]
public class AlbumContent
{
    [ForeignKey(nameof(Album))]
    public Guid AlbumId { get; set; }

    [ForeignKey(nameof(Song))]
    public Guid SongId { get; set; }
    
    // ...
}

[GenericEntity(
    nameof(Album1Id),
    nameof(Album2Id),
    nameof(Album3Id),
    nameof(Album4Id),
    nameof(Album5Id),
    nameof(Album6Id),
    nameof(Album7Id),
    nameof(Album8Id)
)]
public class AlbumSet
{
    [ForeignKey(nameof(Album1))]
    public Guid Album1Id { get; set; }

    [ForeignKey(nameof(Album2))]
    public Guid? Album2Id { get; set; }

    [ForeignKey(nameof(Album3))]
    public Guid? Album3Id { get; set; }

    [ForeignKey(nameof(Album4))]
    public Guid Album4Id { get; set; }

    [ForeignKey(nameof(Album5))]
    public Guid? Album5Id { get; set; }

    [ForeignKey(nameof(Album6))]
    public Guid? Album6Id { get; set; }

    [ForeignKey(nameof(Album7))]
    public Guid? Album7Id { get; set; }

    [ForeignKey(nameof(Album8))]
    public Guid? Album8Id { get; set; }

    // ...
}

Mark models which AutoBackend has to generate API endpoints for

The latest API version is v1. APIv1 supports JSON only, and its output uses contract:

{
  "ok": boolean,
  "error_code": number,
  "description": string,
  "request_time_ms": number,
  "result": object
}

For more details, you can always request /swagger to get the information about all generated endpoints.

❗️❗️❗️ Be noticed that [GenericController] supports only models also marked with [GenericEntity]

Use [GenericController] to generate for:

  • Keyless entity:
    GET /api/v1/<model name> - returns all entities
    GET /api/v1/<model name>/count - returns count of all entities
    POST /api/v1/<model name>/filter - return filtered entities
    POST /api/v1/<model name>/filter/count - return filtered entities count

    [GenericEntity]
    [GenericController]
    public class Note
    {
        // ...
    }
    
  • Entity with a single primary key: Same as for keyless entity:
    GET /api/v1/<model name> - returns all entities
    GET /api/v1/<model name>/count - returns count of all entities
    POST /api/v1/<model name>/filter - return filtered entities
    POST /api/v1/<model name>/filter/count - return filtered entities count

    And extra
    GET /api/v1/<model name>/{key} - returns a specific entity by the primary key
    POST /api/v1/<model name>/{key} - creates a specific entity by the primary key
    PUT /api/v1/<model name>/{key} - updates a specific entity by the primary key
    DELETE /api/v1/<model name>/{key} - deletes a specific entity by the primary key

    [GenericEntity(
        nameof(Id)
    )]
    [GenericController]
    public class Album
    {
        // ...
    }
    
    [GenericEntity(
        nameof(Id)
    )]
    [GenericController]
    public class Song
    {
        // ...
    }
    
  • Entity with a complex primary key: Same as for keyless entity:
    GET /api/v1/<model name> - returns all entities
    GET /api/v1/<model name>/count - returns count of all entities
    POST /api/v1/<model name>/filter - return filtered entities
    POST /api/v1/<model name>/filter/count - return filtered entities count

    And extra
    GET /api/v1/<model name>/{key1}/{key2}/.../{keyN} - returns a specific entity by the complex primary key
    POST /api/v1/<model name>/{key1}/{key2}/.../{keyN} - creates a specific entity by the complex primary key
    PUT /api/v1/<model name>/{key1}/{key2}/.../{keyN} - updates a specific entity by the complex primary key
    DELETE /api/v1/<model name>/{key1}/{key2}/.../{keyN} - deletes a specific entity by the complex primary key

    [GenericEntity(
      nameof(AlbumId),
      nameof(SongId)
    )]
    [GenericController]
    public class AlbumContent
    {
        // ...
    }
    
    [GenericEntity(
      nameof(Album1Id),
      nameof(Album2Id),
      nameof(Album3Id),
      nameof(Album4Id),
      nameof(Album5Id),
      nameof(Album6Id),
      nameof(Album7Id),
      nameof(Album8Id)
    )]
    [GenericController]
    public class AlbumSet
    {
        // ...
    }
    

Mark model properties which AutoBackend has to generate filters for

  • Use [GenericFilter] to mark the model property as a property that the generated entity can be filtered by.
[GenericEntity(
    nameof(Id)
)]
[GenericController]
public class Album
{
    [GenericFilter] public Guid Id { get; set; }

    [GenericFilter] public string Title { get; set; } = null!;

    [GenericFilter] public string? Artist { get; set; }

    [GenericFilter] public int Score { get; set; } = 0;
}

As a result, AutoBackend will build a filter model that you can use in the API endpoints, such as /api/v1/<model name>/filter or /api/v1/<model name>/filter/count. For the example above, the filter model looks like that:

{
  "id": {
    "equal": string | null | undefined,
    "notEqual": string | null | undefined,
    "isNull": boolean | null | undefined,
    "greaterThan": string | null | undefined,
    "greaterThanOrEqual": string | null | undefined,
    "lessThan": string | null | undefined,
    "lessThanOrEqual": string | null | undefined,
    "in": [
      string | null | undefined,
      string | null | undefined
    ]
  },
  "title": {
    "equal": string | null | undefined,
    "notEqual": string | null | undefined,
    "isNull": boolean | null | undefined,
    "greaterThan": string | null | undefined,
    "greaterThanOrEqual": string | null | undefined,
    "lessThan": string | null | undefined,
    "lessThanOrEqual": string | null | undefined,
    "in": [
      string | null | undefined,
      string | null | undefined
    ]
  },
  "artist": {
    "equal": string | null | undefined,
    "notEqual": string | null | undefined,
    "isNull": boolean | null | undefined,
    "greaterThan": string | null | undefined,
    "greaterThanOrEqual": string | null | undefined,
    "lessThan": string | null | undefined,
    "lessThanOrEqual": string | null | undefined,
    "in": [
      string | null | undefined,
      string | null | undefined
    ]
  },
  "score": {
    "equal": number | null | undefined,
    "notEqual": number | null | undefined,
    "isNull": boolean | null | undefined,
    "greaterThan": number | null | undefined,
    "greaterThanOrEqual": number | null | undefined,
    "lessThan": number | null | undefined,
    "lessThanOrEqual": number | null | undefined,
    "in": [
      number | null | undefined,
      number | null | undefined
    ]
  }
}

It contains properties with the same names as the model properties marked with [GenericFilter]. Each property is an object with filter parameters. You can see that each filter parameter type is the same as the model property marked with the [GenericFilter]. All filter parameters are optional.

Make sure you have configured your application database connection correctly

To manage database connections, choose the "Database" configuration section.

"Database": {
  "PrimaryProvider": "InMemory",
  "Providers": {
    "InMemory": "InMemory database name",
    "SqlServer": "SqlServer database connection string",
    "Postgres": "Postgres database connection string"
  }
}

The property PrimaryProvider allows one of the following string values: InMemory, SqlServer, or Postgres. The property Providers shall contain an object with optional properties: InMemory, SqlServer, or Postgres. If you chose the PrimaryProvider value, the property with the same name as that value is required.

For example:

  • If you would like to use the InMemory database, you shall fill in the "Database" configuration section like this:
    "Database": {
      "PrimaryProvider": "InMemory",
      "Providers": {
        "InMemory": "<InMemory database name>",
      }
    }
    
  • If you would like to use the InMemory database, you shall fill in the "Database" configuration section like this:
    "Database": {
      "PrimaryProvider": "SqlServer",
      "Providers": {
        "SqlServer": "<SqlServer database connection string>",
      }
    }
    
  • If you would like to use the InMemory database, you shall fill in the "Database" configuration section like this:
    "Database": {
      "PrimaryProvider": "Postgres",
      "Providers": {
        "Postgres": "<Postgres database connection string in the Npgsql format>",
      }
    }
    

Make sure you have configured your application startup correctly

Suppose you use a relational database (like SqlServer or Postgres). In that case, you can let AutoBackend know whether it has to migrate the database automatically on the application startup or doesn't, passing the migrateRelationalOnStartup parameter to the RunAsync method in the place you call it to initialize AutoBackend. Here is an example:

await new AutoBackend.Sdk.AutoBackendHost<Program>().RunAsync(args, migrateRelationalOnStartup: true);

Migrate the database schema

First, you must install Entity Framework Core Tools. After that, you can create a new migration using one of the following commands executed from the root of the project folder.

dotnet ef migrations add "<your migration name>" -o Migrations/SqlServer -c SqlServerGenericDbContext - if you use SqlServer.

dotnet ef migrations add "<your migration name>" -o Migrations/Postgres -c PostgresGenericDbContext - if you use Postgres.

Or you can create scripts for adding a new migration or removing the last migration for both database providers.

Finally, suppose you did not choose to delegate the database migrating to AutoBackend (see above). In that case, you can migrate it yourself, executing dotnet ef database update from the root of the project folder.

Be noticed of best practices

GenericDbContext is way better than any other

Despite the database provider you have chosen, if you need to access the DbContext directly from your code, it will be the best practice to inject the GenericDbContext into your services. This way, you can switch between any database providers offered by AutoBackend.SDK simply by changing one line in your application config.

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.9 89 4/27/2024
0.1.5 232 3/11/2023

This is the public release `v0.1.5` of the package `AutoBackend.SDK`. This package provides the boilerplate infrastructure to create simplified backend services by managing DataBase and API layers. For more information, see [readme](https://github.com/vorobalek/autobackend/blob/main/README.md).

Developer builds are published on the [specific MyGet developer feed](https://www.myget.org/feed/autobackend-dev/package/nuget/AutoBackend.SDK). Release builds are published on [MyGet](https://www.myget.org/feed/autobackend/package/nuget/AutoBackend.SDK) and [NuGet](AutoBackend.SDK).

In this version:

- The only objects visible as public now are:
   - `GenericControllerAttribute`
   - `GenericEntityAttribute`
   - `GenericFilterAttribute`
   - `GenericDbContext` and inheritors (but it's not recommended to use them)
       - `InMemoryGenericDbContext`
       - `PostgresGenericDbContext`
       - `SqlServerGenericDbContext`
   - `AutoBackendException`
   - `AutoBackendHost`
- Inheritance from any library's publicly visible type has been restricted
- `AutoBackendDbContext` has been renamed to `GenericDbContext`
- `AutoBackendDbContext<TContext>` has been removed permanently
- `GenericControllerV1Attribute` has been removed permanently
- Initializing `AutoBackendException` outside the library has been restricted
- `GenericEntityAttribute.Keys` visibility has been changed to `internal`
- Refactoring
   - Namespaces have been simplified
   - Excess files have been removed
- Bug fixes
   - Wrong database provider determination has been fixed