Skip to content

Commit

Permalink
Implement SqlServer query builder.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohsen Esmailpour committed Sep 22, 2024
1 parent 9127ebf commit 3120c2b
Show file tree
Hide file tree
Showing 17 changed files with 520 additions and 392 deletions.
45 changes: 30 additions & 15 deletions src/Serilog.Ui.Core/QueryBuilder/Sql/SqlQueryBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
using Serilog.Ui.Core.Models;
using Serilog.Ui.Core.Attributes;
using Serilog.Ui.Core.Models;
using System.Reflection;
using static Serilog.Ui.Core.Models.SearchOptions;

namespace Serilog.Ui.Core.QueryBuilder.Sql;

/// <summary>
/// Abstract class that provides methods to build SQL queries for fetching and counting logs.
/// </summary>
public abstract class SqlQueryBuilder
public abstract class SqlQueryBuilder<TModel> where TModel : LogModel
{
/// <summary>
/// Builds a SQL query to fetch logs from the specified table.
/// </summary>
/// <typeparam name="T">The type of the log model.</typeparam>
/// <param name="columns">The column names used in the sink for logging.</param>
/// <param name="schema">The schema of the table.</param>
/// <param name="tableName">The name of the table.</param>
/// <param name="query">The query parameters for fetching logs.</param>
/// <returns>A SQL query string to fetch logs.</returns>
public abstract string BuildFetchLogsQuery<T>(SinkColumnNames columns, string schema, string tableName, FetchLogsQuery query) where T : LogModel;
public abstract string BuildFetchLogsQuery(SinkColumnNames columns, string schema, string tableName, FetchLogsQuery query);

/// <summary>
/// Builds a SQL query to count logs in the specified table.
/// </summary>
/// <typeparam name="T">The type of the log model.</typeparam>
/// <param name="columns">The column names used in the sink for logging.</param>
/// <param name="schema">The schema of the table.</param>
/// <param name="tableName">The name of the table.</param>
/// <param name="query">The query parameters for counting logs.</param>
/// <returns>A SQL query string to count logs.</returns>
public abstract string BuildCountLogsQuery<T>(SinkColumnNames columns, string schema, string tableName, FetchLogsQuery query) where T : LogModel;
public abstract string BuildCountLogsQuery(SinkColumnNames columns, string schema, string tableName, FetchLogsQuery query);

/// <summary>
/// Generates a SQL sort clause based on the specified sort property and direction.
Expand All @@ -37,16 +37,31 @@ public abstract class SqlQueryBuilder
/// <param name="sortOn">The property to sort on.</param>
/// <param name="sortBy">The direction to sort by.</param>
/// <returns>A SQL sort clause string.</returns>
protected static string GenerateSortClause(SinkColumnNames columns, SortProperty sortOn, SortDirection sortBy)
protected abstract string GenerateSortClause(SinkColumnNames columns, SortProperty sortOn, SortDirection sortBy);

/// <summary>
/// Generates a SQL sort clause based on the specified sort property and direction.
/// </summary>
/// <param name="columns">The column names used in the sink for logging.</param>
/// <param name="sortOn">The property to sort on.</param>
/// <returns>A SQL sort clause string.</returns>
protected static string GetSortColumnName(SinkColumnNames columns, SortProperty sortOn) => sortOn switch
{
SortProperty.Timestamp => columns.Timestamp,
SortProperty.Level => columns.Level,
SortProperty.Message => columns.Message,
_ => columns.Timestamp
};

/// <summary>
/// Determines whether to add the exception column to the WHERE clause based on the presence of the RemovedColumnAttribute.
/// </summary>
/// <returns>True if the exception column should be added to the WHERE clause; otherwise, false.</returns>
protected static bool AddExceptionToWhereClause()
{
var sortPropertyName = sortOn switch
{
SortProperty.Timestamp => columns.Timestamp,
SortProperty.Level => columns.Level,
SortProperty.Message => columns.Message,
_ => columns.Timestamp
};
PropertyInfo? exceptionProperty = typeof(TModel).GetProperty("Exception");
RemovedColumnAttribute? att = exceptionProperty?.GetCustomAttribute<RemovedColumnAttribute>();

return $"\"{sortPropertyName}\" {sortBy.ToString().ToUpper()}";
return att is null;
}
}
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
using System;
using Dapper;
using Dapper;
using Microsoft.Extensions.DependencyInjection;
using Serilog.Ui.Core;
using Serilog.Ui.Core.Interfaces;
using Serilog.Ui.Core.Models.Options;
using System;

namespace Serilog.Ui.MsSqlServerProvider.Extensions
{
/// <summary>
/// SQL Server data provider specific extension methods for <see cref="ISerilogUiOptionsBuilder"/>.
/// </summary>
public static class SerilogUiOptionBuilderExtensions
{
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
/// <param name="optionsBuilder"> The options builder. </param>
/// <param name="setupOptions">The Ms Sql options action.</param>
/// <param name="dateTimeCustomParsing">
/// Delegate to customize the DateTime parsing.
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
/// </param>
public static ISerilogUiOptionsBuilder UseSqlServer(
this ISerilogUiOptionsBuilder optionsBuilder,
Action<RelationalDbOptions> setupOptions,
Func<string, DateTime>? dateTimeCustomParsing = null
) => optionsBuilder.UseSqlServer<SqlServerLogModel>(setupOptions, dateTimeCustomParsing);

/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
/// <typeparam name="T">The log model, containing any additional columns. It must inherit <see cref="SqlServerLogModel"/>.</typeparam>
/// <param name="optionsBuilder"> The options builder. </param>
/// <param name="setupOptions">The Ms Sql options action.</param>
/// <param name="dateTimeCustomParsing">
/// Delegate to customize the DateTime parsing.
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
/// </param>
public static ISerilogUiOptionsBuilder UseSqlServer<T>(
this ISerilogUiOptionsBuilder optionsBuilder,
Action<RelationalDbOptions> setupOptions,
Func<string, DateTime>? dateTimeCustomParsing = null
) where T : SqlServerLogModel
{
var dbOptions = new RelationalDbOptions("dbo");
setupOptions(dbOptions);
dbOptions.Validate();

var providerName = dbOptions.GetProviderName(SqlServerDataProvider.MsSqlProviderName);
namespace Serilog.Ui.MsSqlServerProvider.Extensions;

optionsBuilder.RegisterExceptionAsStringForProviderKey(providerName);
SqlMapper.AddTypeHandler(new DapperDateTimeHandler(dateTimeCustomParsing));
/// <summary>
/// SQL Server data provider specific extension methods for <see cref="ISerilogUiOptionsBuilder"/>.
/// </summary>
public static class SerilogUiOptionBuilderExtensions
{
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
/// <param name="optionsBuilder"> The options builder. </param>
/// <param name="setupOptions">The Ms Sql options action.</param>
/// <param name="dateTimeCustomParsing">
/// Delegate to customize the DateTime parsing.
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
/// </param>
public static ISerilogUiOptionsBuilder UseSqlServer(
this ISerilogUiOptionsBuilder optionsBuilder,
Action<RelationalDbOptions> setupOptions,
Func<string, DateTime>? dateTimeCustomParsing = null
) => optionsBuilder.UseSqlServer<SqlServerLogModel>(setupOptions, dateTimeCustomParsing);

var customModel = typeof(T) != typeof(SqlServerLogModel);
if (customModel)
{
optionsBuilder.RegisterColumnsInfo<T>(providerName);
optionsBuilder.Services.AddScoped<IDataProvider>(_ => new SqlServerDataProvider<T>(dbOptions));
/// <summary>Configures the SerilogUi to connect to a SQL Server database.</summary>
/// <typeparam name="T">The log model, containing any additional columns. It must inherit <see cref="SqlServerLogModel"/>.</typeparam>
/// <param name="optionsBuilder"> The options builder. </param>
/// <param name="setupOptions">The Ms Sql options action.</param>
/// <param name="dateTimeCustomParsing">
/// Delegate to customize the DateTime parsing.
/// It throws <see cref="InvalidOperationException" /> if the return DateTime isn't UTC kind.
/// </param>
public static ISerilogUiOptionsBuilder UseSqlServer<T>(
this ISerilogUiOptionsBuilder optionsBuilder,
Action<RelationalDbOptions> setupOptions,
Func<string, DateTime>? dateTimeCustomParsing = null
) where T : SqlServerLogModel
{
SqlServerDbOptions dbOptions = new("dbo");
setupOptions(dbOptions);
dbOptions.Validate();

return optionsBuilder;
}
string providerName = dbOptions.GetProviderName(SqlServerDataProvider.MsSqlProviderName);
optionsBuilder.RegisterExceptionAsStringForProviderKey(providerName);
SqlMapper.AddTypeHandler(new DapperDateTimeHandler(dateTimeCustomParsing));

optionsBuilder.Services.AddScoped<IDataProvider>(_ => new SqlServerDataProvider(dbOptions));
return optionsBuilder;
bool customModel = typeof(T) != typeof(SqlServerLogModel);
if (customModel)
{
optionsBuilder.RegisterColumnsInfo<T>(providerName);
optionsBuilder.Services.AddScoped<IDataProvider>(_ => new SqlServerDataProvider<T>(dbOptions, new SqlServerQueryBuilder<T>()));
}
else
{
optionsBuilder.Services.AddScoped<IDataProvider>(_ =>
new SqlServerDataProvider(dbOptions, new SqlServerQueryBuilder<SqlServerLogModel>()));
}

return optionsBuilder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Serilog.Ui.Core.Models.Options;
using Serilog.Ui.Core.QueryBuilder.Sql;
using Serilog.Ui.MsSqlServerProvider.Models;

namespace Serilog.Ui.MsSqlServerProvider.Extensions;

public class SqlServerDbOptions(string defaultSchemaName) : RelationalDbOptions(defaultSchemaName)
{
internal SinkColumnNames ColumnNames { get; } = new SqlServerSinkColumnNames();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Serilog.Ui.Core.QueryBuilder.Sql;

namespace Serilog.Ui.MsSqlServerProvider.Models;

internal class SqlServerSinkColumnNames : SinkColumnNames
{
public SqlServerSinkColumnNames()
{
Exception = "Exception";
Level = "Level";
LogEventSerialized = "Properties";
Message = "Message";
MessageTemplate = "";
Timestamp = "TimeStamp";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1"/>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Serilog.Ui.Core\Serilog.Ui.Core.csproj" PrivateAssets="All" />
<InternalsVisibleTo Include="MsSql.Tests" />
</ItemGroup>
</Project>
Loading

0 comments on commit 3120c2b

Please sign in to comment.