tbf/TBF/Rig/Input/DataStorage/UniDataStorageReader/Readers/DatabaseReader .cs

245 lines
8.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using TBF.Rig.Input.DataStorage.UniDataStorageReader.Searching;
using TBF.Rig.Input.DataStorage.UniDataStorageReader.Searching.Database;
namespace TBF.Rig.Input.DataStorage.UniDataStorageReader.Readers
{
/// <summary>
/// Reader implementation for SQL Server based data source.
/// DataSource = SQL Server connection string
/// QueryTemplate = SQL query containing QUERYPARAM placeholder
/// </summary>
public class DatabaseReader : IDataStorageReader
{
private readonly ReaderCfg cfg;
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
{
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
if (string.IsNullOrWhiteSpace(cfg.DataSource))
{
result.Success = false;
result.Message = "Data source is empty.";
return result;
}
if (enableDiagnostics)
result.Diagnostics.Add("Opening SQL connection...");
using (SqlConnection connection = new SqlConnection(cfg.DataSource))
{
connection.Open();
if (enableDiagnostics)
result.Diagnostics.Add("Connection opened successfully.");
using (SqlCommand command = new SqlCommand("SELECT 1", connection))
{
object val = command.ExecuteScalar();
if (enableDiagnostics)
result.Diagnostics.Add("Test query executed. Result=" + val);
}
}
result.Success = true;
result.Message = "Connection to SQL Server OK.";
return result;
}
catch (Exception ex)
{
result.Success = false;
result.Message = "Failed to connect to SQL Server.";
if (enableDiagnostics)
result.Diagnostics.Add(ex.ToString());
return result;
}
}
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
{
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
if (string.IsNullOrWhiteSpace(cfg.QueryTemplate))
{
result.Success = false;
result.Message = "Query template is empty.";
return result;
}
string sql = PrepareSqlText(cfg.QueryTemplate);
if (enableDiagnostics)
{
result.Diagnostics.Add("Original template:");
result.Diagnostics.Add(cfg.QueryTemplate);
result.Diagnostics.Add("Prepared SQL:");
result.Diagnostics.Add(sql);
}
using (SqlConnection connection = new SqlConnection(cfg.DataSource))
using (SqlCommand command = new SqlCommand(sql, connection))
{
// dummy parameter
command.Parameters.AddWithValue("@value", "TEST");
if (enableDiagnostics)
result.Diagnostics.Add("Parameter @value = TEST");
connection.Open();
object val = command.ExecuteScalar();
if (enableDiagnostics)
result.Diagnostics.Add("Query executed successfully.");
result.Data = val; // can be null → OK
}
result.Success = true;
result.Message = "Query executed successfully.";
return result;
}
catch (Exception ex)
{
result.Success = false;
result.Message = "Query execution failed.";
if (enableDiagnostics)
result.Diagnostics.Add(ex.ToString());
return result;
}
}
public DatabaseReader(ReaderCfg cfg)
{
this.cfg = cfg ?? throw new ArgumentNullException(nameof(cfg));
}
/// <summary>
/// Reads data from SQL Server and returns matched data.
/// If query returns 1 column, scalar value is returned.
/// If query returns multiple columns, Dictionary&lt;string, object&gt; is returned.
/// </summary>
public object GetData(Interfaces.PublicModels.DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
ReaderDiagnosticResult connectResult = ConnectToSource(true);
if (!connectResult.Success)
throw new InvalidOperationException(connectResult.Message);
string sqlText = PrepareSqlText(cfg.QueryTemplate);
object queryValue = ExtractQueryValue(query);
using (SqlConnection connection = new SqlConnection(cfg.DataSource))
using (SqlCommand command = new SqlCommand(sqlText, connection))
{
AddQueryParameters(command, queryValue);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow))
{
DatabaseSearchResult result = new DatabaseSearchResult();
result.Query = sqlText;
if (!reader.Read())
{
result.Found = false;
return result;
}
result.Found = true;
for (int i = 0; i < reader.FieldCount; i++)
{
object value = reader.GetValue(i);
result.Values[reader.GetName(i)] = value == DBNull.Value ? null : value;
}
return result;
}
}
}
/// <summary>
/// Validates database connectivity and basic query readiness.
/// </summary>
public ReaderDiagnosticResult ConnectToSource(bool validateExistence)
{
if (string.IsNullOrWhiteSpace(cfg.DataSource))
return ReaderDiagnosticResult.Failure("Data source must not be empty.");
if (string.IsNullOrWhiteSpace(cfg.QueryTemplate))
return ReaderDiagnosticResult.Failure("Query template must not be empty.");
try
{
using (SqlConnection connection = new SqlConnection(cfg.DataSource))
{
connection.Open();
if (validateExistence)
{
using (SqlCommand command = new SqlCommand("SELECT 1", connection))
{
command.ExecuteScalar();
}
}
}
return ReaderDiagnosticResult.SuccessResult();
}
catch (Exception ex)
{
return ReaderDiagnosticResult.Failure(
string.Format("Failed to connect to SQL Server data source. {0}", ex.Message));
}
}
private static string PrepareSqlText(string queryTemplate)
{
if (string.IsNullOrWhiteSpace(queryTemplate))
throw new ArgumentException("Query template must not be empty.", nameof(queryTemplate));
if (!queryTemplate.Contains("QUERYPARAM"))
throw new InvalidOperationException("Query template must contain QUERYPARAM placeholder.");
return queryTemplate.Replace("QUERYPARAM", "@value");
}
private static void AddQueryParameters(SqlCommand command, object queryValue)
{
command.Parameters.Clear();
SqlParameter parameter = command.Parameters.Add("@value", SqlDbType.Variant);
parameter.Value = queryValue ?? DBNull.Value;
}
private static object ExtractQueryValue(Interfaces.PublicModels.DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
if (query.QueryParams == null || query.QueryParams.Count == 0)
throw new InvalidOperationException("DataQuery does not contain any query parameter.");
return query.QueryParams[0];
}
}
}