237 lines
12 KiB
C#
237 lines
12 KiB
C#
#pragma warning disable 1591
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging;
|
|
using MySqlConnector;
|
|
using Newtonsoft.Json;
|
|
|
|
// make sure to adjust the prefix with the PACKAGE_NAME from ENV
|
|
using com.krohne.genericdatabaseapiservice.Models;
|
|
|
|
// make sure to adjust the prefix with the PACKAGE_NAME from ENV
|
|
namespace com.krohne.genericdatabaseapiservice.Services {
|
|
public class DbInfoObject {
|
|
public DbInfoObject(string host, string user, string password, string name) {
|
|
Host = host;
|
|
User = user;
|
|
Password = password;
|
|
Name = name;
|
|
}
|
|
|
|
public string Host { get; set; }
|
|
public string User { get; set; }
|
|
public string Password { get; set; }
|
|
public string Name { get; set; }
|
|
}
|
|
|
|
public class DbInfoServiceException : Exception {}
|
|
public class UnknownDatabaseTagException: DbInfoServiceException {}
|
|
|
|
public interface IDbInfoService {
|
|
DbInfoObject GetInfoByTag(string tag);
|
|
string GetInfoStringByTag(string tag);
|
|
}
|
|
|
|
public class DbInfoService : IDbInfoService {
|
|
private readonly IConfiguration Configuration;
|
|
private readonly ILogger<DbInfoService> Logger;
|
|
private Dictionary<string, DbInfoObject> DbInfos;
|
|
|
|
public DbInfoService(IConfiguration configuration, ILogger<DbInfoService> logger) {
|
|
Configuration = configuration;
|
|
Logger = logger;
|
|
Console.WriteLine("Database Infofile: {0}", Configuration["Database:InfoFile"]);
|
|
DbInfos = JsonConvert.DeserializeObject<Dictionary<string, DbInfoObject>>(File.ReadAllText(Configuration["Database:InfoFile"]));
|
|
}
|
|
|
|
public DbInfoObject GetInfoByTag(string tag) {
|
|
try {
|
|
return DbInfos[tag];
|
|
} catch (KeyNotFoundException) {
|
|
throw new UnknownDatabaseTagException();
|
|
}
|
|
}
|
|
|
|
public string GetInfoStringByTag(string tag) {
|
|
return String.Format(
|
|
"Server={0};User ID={1};Password={2};Database={3};Convert Zero Datetime=true",
|
|
GetInfoByTag(tag).Host,
|
|
GetInfoByTag(tag).User,
|
|
GetInfoByTag(tag).Password,
|
|
GetInfoByTag(tag).Name);
|
|
}
|
|
}
|
|
|
|
|
|
public interface IDbService {
|
|
Task<List<TOUT>> ReadBySelect<TIN, TOUT>(string databaseTag, string selectStatement, bool justOne, TIN input, bool bindingByStringReplacement);
|
|
}
|
|
|
|
public class DbServiceException : Exception {
|
|
public DbServiceException(string msg): base(msg) { }
|
|
public DbServiceException(): base() { }
|
|
}
|
|
public class NoDataFoundException : DbServiceException {
|
|
public NoDataFoundException(string msg): base(msg) { }
|
|
public NoDataFoundException(): base() { }
|
|
}
|
|
public class TooMuchDataFoundException : DbServiceException {
|
|
public TooMuchDataFoundException(string msg): base(msg) { }
|
|
public TooMuchDataFoundException(): base() { }
|
|
}
|
|
public class UnsupportedDataTypeException: DbServiceException {
|
|
public UnsupportedDataTypeException(string msg): base(msg) { }
|
|
public UnsupportedDataTypeException(): base() { }
|
|
}
|
|
public class StringReplacementNotAllowedException: DbServiceException {
|
|
public StringReplacementNotAllowedException(string msg): base(msg) { }
|
|
public StringReplacementNotAllowedException(): base() { }
|
|
}
|
|
|
|
public class DbService : IDbService {
|
|
private readonly IConfiguration Configuration;
|
|
private readonly ILogger<DbService> Logger;
|
|
private readonly IDbInfoService DbInfoService;
|
|
|
|
public DbService(IConfiguration configuration, ILogger<DbService> logger, IDbInfoService dbInfoService) {
|
|
Configuration = configuration;
|
|
Logger = logger;
|
|
DbInfoService = dbInfoService;
|
|
}
|
|
|
|
async public Task<List<TOUT>> ReadBySelect<TIN, TOUT>(string databaseTag, string selectStatement, bool justOne, TIN input, bool bindingByStringReplacement) {
|
|
var itemList = new List<TOUT>();
|
|
|
|
var databaseConnInfo = DbInfoService.GetInfoStringByTag(databaseTag);
|
|
|
|
Logger.LogInformation("ConnInfo: {0}", databaseConnInfo);
|
|
Logger.LogInformation("Statement: {0}", selectStatement);
|
|
|
|
using (var conn = new MySqlConnection(databaseConnInfo)) {
|
|
await conn.OpenAsync();
|
|
|
|
bool commandTextSet = false;
|
|
using (var cmd = conn.CreateCommand()) {
|
|
if (! bindingByStringReplacement) {
|
|
cmd.CommandText = selectStatement;
|
|
commandTextSet = true;
|
|
}
|
|
if (input != null){
|
|
foreach (var propertyInfo in typeof(TIN).GetProperties()) {
|
|
Logger.LogInformation("Input Property name: {0} {1} ", propertyInfo.Name, propertyInfo.PropertyType);
|
|
var attributes = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), true);
|
|
var dma = (DataMemberAttribute)attributes[0];
|
|
Logger.LogInformation("Input DataMember name: {0} {1}", dma.Name, dma.TypeId);
|
|
var value = propertyInfo.GetValue(input);
|
|
Type typ = value.GetType();
|
|
var isList = typ.IsGenericType && (typ.GetGenericTypeDefinition() == typeof(List<>));
|
|
if (isList) {
|
|
var p1 = propertyInfo.GetValue(input) as System.Collections.IEnumerable;
|
|
var p2 = new StringBuilder();
|
|
var sep = "";
|
|
foreach (var x in p1) {
|
|
if (! (x is int || x is long)) {
|
|
Logger.LogError("Parameter binding by string replacement is for security reasons only allowed for numbers");
|
|
throw new StringReplacementNotAllowedException();
|
|
}
|
|
Logger.LogInformation("x: {0}", x);
|
|
p2.Append(sep);
|
|
p2.Append(x);
|
|
sep = ",";
|
|
}
|
|
var p3 = p2.ToString();
|
|
Logger.LogInformation("Input Value: p3:{0} typ:{1} isList:{2}", p3, typ, isList);
|
|
if (bindingByStringReplacement) {
|
|
if (commandTextSet) {
|
|
Logger.LogError("Parameter bindung by string replacement is only supported for a single parameter");
|
|
throw new StringReplacementNotAllowedException("string replacement only allowed for numeric parameters");
|
|
}
|
|
var processedStatement = selectStatement.Replace(String.Format("@{0}", dma.Name), p3);
|
|
cmd.CommandText = processedStatement;
|
|
commandTextSet = true;
|
|
Logger.LogInformation("Statement after replacement is {0}", processedStatement);
|
|
} else {
|
|
cmd.Parameters.AddWithValue(dma.Name, p3);
|
|
}
|
|
} else {
|
|
if (bindingByStringReplacement) {
|
|
Logger.LogError("Parameter binding by string replacement is only supported for array parameters");
|
|
throw new StringReplacementNotAllowedException("string replacement only allowed to handle lists of parameters");
|
|
}
|
|
Logger.LogInformation("Input Value: {0} {1} {2}", value, typ, isList);
|
|
cmd.Parameters.AddWithValue(dma.Name, value);
|
|
}
|
|
}
|
|
} else {
|
|
Logger.LogInformation("no input data");
|
|
}
|
|
using (var reader = await cmd.ExecuteReaderAsync()) {
|
|
while (await reader.ReadAsync()) {
|
|
var item = Activator.CreateInstance<TOUT>();
|
|
foreach (var propertyInfo in typeof(TOUT).GetProperties()) {
|
|
Logger.LogInformation("Output Property name: {0} {1} ", propertyInfo.Name, propertyInfo.PropertyType);
|
|
var attributes = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), true);
|
|
var dma = (DataMemberAttribute)attributes[0];
|
|
int ordinal = reader.GetOrdinal(dma.Name);
|
|
Logger.LogInformation("Output DataMember name: {0} {1} {2} ", dma.Name, dma.TypeId, ordinal);
|
|
if (await reader.IsDBNullAsync(ordinal)) {
|
|
propertyInfo.SetValue(item, null);
|
|
Logger.LogInformation("Output Value: null");
|
|
} else if (propertyInfo.PropertyType == typeof(System.String)) {
|
|
var value = reader.GetString(ordinal);
|
|
propertyInfo.SetValue(item, value);
|
|
Logger.LogInformation("Output Value:{0}", value);
|
|
} else if (propertyInfo.PropertyType == typeof(System.Int32) ||
|
|
propertyInfo.PropertyType == typeof(System.Nullable<System.Int32>)) {
|
|
var value = reader.GetInt32(ordinal);
|
|
propertyInfo.SetValue(item, value);
|
|
Logger.LogInformation("Output Value:{0}", (System.Int32)value);
|
|
} else if (propertyInfo.PropertyType == typeof(System.Int64) ||
|
|
propertyInfo.PropertyType == typeof(System.Nullable<System.Int64>)) {
|
|
var value = reader.GetInt64(ordinal);
|
|
propertyInfo.SetValue(item, value);
|
|
Logger.LogInformation("Output Value:{0}", (System.Int64)value);
|
|
} else if (propertyInfo.PropertyType == typeof(System.DateTime)) {
|
|
var value = reader.GetDateTime(ordinal);
|
|
propertyInfo.SetValue(item, value);
|
|
Logger.LogInformation("Output Value:{0}", value);
|
|
} else if (propertyInfo.PropertyType == typeof(System.Boolean) ||
|
|
propertyInfo.PropertyType == typeof(System.Nullable<System.Boolean>)) {
|
|
var value = reader.GetBoolean(ordinal);
|
|
propertyInfo.SetValue(item, value);
|
|
Logger.LogInformation("Output Value:{0}", value);
|
|
} else {
|
|
Logger.LogError("Unsupported data type {0}", propertyInfo.PropertyType);
|
|
throw new UnsupportedDataTypeException(String.Format("{0}", propertyInfo.PropertyType));
|
|
}
|
|
}
|
|
itemList.Add(item);
|
|
Logger.LogInformation("Item is {0}", item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (itemList.Count == 0) {
|
|
Logger.LogWarning("no data found");
|
|
throw new NoDataFoundException();
|
|
}
|
|
if (justOne && itemList.Count > 1) {
|
|
Logger.LogWarning("too much data found");
|
|
throw new TooMuchDataFoundException();
|
|
}
|
|
|
|
return itemList;
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma warning restore 1591
|