Byndyusoft.Data.Relational.QueryBuilder
2.2.0
dotnet add package Byndyusoft.Data.Relational.QueryBuilder --version 2.2.0
NuGet\Install-Package Byndyusoft.Data.Relational.QueryBuilder -Version 2.2.0
<PackageReference Include="Byndyusoft.Data.Relational.QueryBuilder" Version="2.2.0" />
paket add Byndyusoft.Data.Relational.QueryBuilder --version 2.2.0
#r "nuget: Byndyusoft.Data.Relational.QueryBuilder, 2.2.0"
// Install Byndyusoft.Data.Relational.QueryBuilder as a Cake Addin #addin nuget:?package=Byndyusoft.Data.Relational.QueryBuilder&version=2.2.0 // Install Byndyusoft.Data.Relational.QueryBuilder as a Cake Tool #tool nuget:?package=Byndyusoft.Data.Relational.QueryBuilder&version=2.2.0
Byndyusoft.Data.Relational.QueryBuilder
This library allows to create SQL queries.
The main concept of the library is to avoid using string constants for column names in queries. This would allow to be more confident and peaceful while performing code refactoring. Additionally, this can reduce the need to check if queries are written correctly.
For standard queries that involve all columns (SELECT, UPDATE, INSERT), you won't need to worry about adding or deleting a new column.
Below are the descriptions of standard use cases.
Installing
dotnet add package Byndyusoft.Data.Relational.QueryBuilder
Usage
Data model for examples
We will use the following data model for the examples.
public class Company : IEntity
{
public long Id { get; set; }
public string Name { get; set; } = default!;
public string Inn { get; set; } = default!;
}
public class User : IEntity
{
public long Id { get; set; }
public string Login { get; set; } = default!;
public string Password { get; set; } = default!;
public long CompanyId { get; set; }
}
Any type can be used for the primary key for queries. If the IEntity interface is used for models, where the primary key is defined as long, some queries will be easier, for example, inserting all fields, updating all fields, filtering by ID.
The library will treat all public properties with existing get and set methods as table columns. Their names will correspond to the column names (SnakeCase formatting is used for PostgreSQL).
Select
Querying an entity from a table.
You need to create a SelectQuery object to create a SELECT query. Here is an example for the Company entity:
public class SelectQuery : SelectQueryBuilderBase<SelectQuery>
{
protected override void PrepareFrom()
{
FromCollector.From<Company>(TableNames.Company, Aliases.Company);
}
protected override void PrepareSelect()
{
SelectCollector.To<Company>(Aliases.Company).GetAllPublicValues();
}
public SelectQuery ById(long id)
{
Conditionals.For<Company>(Aliases.Company).ById(id);
return this;
}
public SelectQuery ByName(string name)
{
Conditionals.For<Company>(Aliases.Company).AddEquals(i => i.Name, name);
return this;
}
}
Example queries to retrieve a Company by ID and by name:
public class CompanyRepository : DbSessionConsumer
{
public CompanyRepository(IDbSessionAccessor sessionAccessor) : base(sessionAccessor)
{
}
public async Task<Company?> GetById(long id, CancellationToken cancellationToken)
{
var queryObject = new SelectQuery().ById(id).Build();
return await DbSession.QuerySingleOrDefaultAsync(queryObject, cancellationToken: cancellationToken);
}
public async Task<Company[]> GetByName(string name, CancellationToken cancellationToken)
{
var queryObject = new SelectQuery().ByName(name).Build();
var companies = await DbSession.QueryAsync<Company>(queryObject, cancellationToken: cancellationToken);
return companies.ToArray();
}
}
Query from multiple tables with data projection into DTO
Let's assume we need to obtain information about a user in the form of the following object:
public class UserDto
{
public string Login { get; set; } = default!;
public string Password { get; set; } = default!;
public string CompanyName { get; set; } = default!;
}
Example of a repository and the SelectQuery object:
public class UserDtoRepository : DbSessionConsumer
{
public UserDtoRepository(IDbSessionAccessor sessionAccessor) : base(sessionAccessor)
{
}
public async Task<UserDto?> GetByIdAsync(long id, CancellationToken cancellationToken)
{
var queryObject = new SelectQuery().ById(id).Build();
return await DbSession.QuerySingleOrDefaultAsync<UserDto>(queryObject,
cancellationToken: cancellationToken);
}
public class SelectQuery : SelectQueryBuilderBase<SelectQuery>
{
protected override void PrepareFrom()
{
FromCollector
.From<User>(TableNames.Users, Aliases.Users)
.InnerJoin(TableNames.Company, Aliases.Company, i => i.CompanyId);
}
protected override void PrepareSelect()
{
SelectCollector.To<UserDto>()
.From<User>(Aliases.Users)
.Get(u => u.Login, dto => dto.Login)
.Get(u => u.Password, dto => dto.Password)
.Other<Company>(Aliases.Company)
.Get(c => c.Name, dto => dto.CompanyName);
}
public SelectQuery ById(long id)
{
Conditionals.For<User>(Aliases.Users).ById(id);
return this;
}
}
}
Query from multiple tables with aggregation and sorting
Let's suppose we need to obtain information about a list of companies with the number of users:
public class CompanyReportDto
{
public string CompanyName { get; set; } = default!;
public long UserCount { get; set; }
}
Example of a repository and the SelectQuery object:
public class CompanyReportDtoRepository : DbSessionConsumer
{
public CompanyReportDtoRepository(IDbSessionAccessor sessionAccessor) : base(sessionAccessor)
{
}
public async Task<CompanyReportDto[]> GetAsync(CancellationToken cancellationToken)
{
var queryObject = new SelectQuery().Build();
var companyReportDtos = await DbSession.QueryAsync<CompanyReportDto>(queryObject, cancellationToken: cancellationToken);
return companyReportDtos.ToArray();
}
public class SelectQuery : SelectQueryBuilderBase<SelectQuery>
{
public SelectQuery()
{
GroupBy.For<Company>(Aliases.Company).Add(i => i.Name);
OrderBy.Add<Company, string>(i => i.Name, isDescending: false, Aliases.Company);
}
protected override void PrepareFrom()
{
FromCollector
.From<Company>(TableNames.Company, Aliases.Company)
.LeftJoin<User>(TableNames.Users, Aliases.Users, (c, u) => $"{c.Id} = {u.CompanyId}");
}
protected override void PrepareSelect()
{
SelectCollector.To<CompanyReportDto>()
.Get<User, long>(
dto => dto.UserCount,
u => $"SUM(CASE WHEN {u.Id} IS NULL THEN 0 ELSE 1 END)",
Aliases.Users)
.From<Company>(Aliases.Company)
.Get(c => c.Name, dto => dto.CompanyName);
}
}
}
Insert
Example of inserting a Company:
public async Task InsertAsync(Company company, CancellationToken cancellationToken)
{
var queryObject = InsertQueryBuilder<Company>
.For(company, TableNames.Company)
.InsertAllPublicValues()
.Build();
var id = await DbSession.ExecuteScalarAsync<long>(queryObject, cancellationToken: cancellationToken);
company.Id = id;
}
Update
Updating all fields
Example of updating all Company fields:
public async Task UpdateAsync(Company company, CancellationToken cancellationToken)
{
var queryObject = UpdateItemQueryBuilder<Company>
.For(company, TableNames.Company)
.UpdateAllPublicValues()
.ById()
.Build();
await DbSession.ExecuteAsync(queryObject, cancellationToken: cancellationToken);
}
Updating a single field
Example of updating the Company's tax identification number (INN):
public async Task UpdateInnAsync(long id, string inn, CancellationToken cancellationToken)
{
var queryObject = UpdateQueryBuilder<Company>
.For(TableNames.Company)
.Set(i => i.Inn, inn)
.ById(id)
.Build();
await DbSession.ExecuteAsync(queryObject, cancellationToken: cancellationToken);
}
Delete
Example of deleting a Company:
public async Task DeleteByIdAsync(long id, CancellationToken cancellationToken)
{
var queryObject = DeleteQueryBuilder<Company>
.For(TableNames.Company)
.ById(id)
.Build();
await DbSession.ExecuteAsync(queryObject, cancellationToken: cancellationToken);
}
Custom queries using ColumnConverter
An alternative example of fetching the UserDto, as written above:
public async Task<UserDto?> GetByIdAlternativelyAsync(long id, CancellationToken cancellationToken)
{
var columnConverter = new ColumnConverter(true);
var sql = columnConverter.Map<User, Company>((user, company) => $@"
SELECT
{user.Login} AS {nameof(UserDto.Login)},
{user.Password} AS {nameof(UserDto.Password)},
{company.Name} AS {nameof(UserDto.CompanyName)}
FROM
{TableNames.Users} u
INNER JOIN {TableNames.Company} c on {company.Id} = {user.CompanyId}
WHERE
{user.Id} = @UserId", "u", "c");
var queryObject = new QueryObject(sql, new { UserId = id });
return await DbSession.QuerySingleOrDefaultAsync<UserDto>(queryObject,
cancellationToken: cancellationToken);
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. 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. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Byndyusoft.Data.Relational.Abstractions (>= 0.8.0)
- Byndyusoft.Data.Relational.QueryBuilder.Abstractions (>= 2.2.0)
- Npgsql (>= 8.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.