초기커밋
This commit is contained in:
82
ServerBase/DB/DynamoDb/AttribBase.cs
Normal file
82
ServerBase/DB/DynamoDb/AttribBase.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public abstract partial class AttribBase
|
||||
{
|
||||
// AttribType은 DynamoDb Document 정보를 Read시 식별키로 사용되기 때문에 고유하게 정의되어야 한다 !!! - kangms
|
||||
[JsonProperty("attrib_type")]
|
||||
public readonly string AttribType;
|
||||
|
||||
private readonly bool m_is_save_to_json_in_db = false;
|
||||
|
||||
public AttribBase(string attribType, bool isSaveToJsonInDb = true)
|
||||
{
|
||||
AttribType = attribType;
|
||||
m_is_save_to_json_in_db = isSaveToJsonInDb;
|
||||
}
|
||||
|
||||
public bool isSaveToJsonInDb() => m_is_save_to_json_in_db;
|
||||
|
||||
public virtual string toJsonString()
|
||||
{
|
||||
return JsonConvert.SerializeObject(this);
|
||||
}
|
||||
|
||||
public virtual Amazon.DynamoDBv2.DocumentModel.Document toDocument()
|
||||
{
|
||||
return DynamoDbClientHelper.classToDynamoDbDocument(this);
|
||||
}
|
||||
public virtual string toBasicString()
|
||||
{
|
||||
return $"AttribType:{AttribType}, {toJsonString()}";
|
||||
}
|
||||
}
|
||||
|
||||
public partial class AttribWrapper<TAttrib> : IAttribWrapper
|
||||
where TAttrib : AttribBase, new()
|
||||
{
|
||||
private TAttrib m_attrib = new();
|
||||
|
||||
public AttribWrapper()
|
||||
{}
|
||||
|
||||
public bool onCopyFromDynamoDbEntry(Amazon.DynamoDBv2.DocumentModel.Document doc)
|
||||
{
|
||||
if(false == doc.fillupAttribObject<TAttrib>(out var fillup_attrib))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fillup_attrib == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_attrib = fillup_attrib;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool onCopyToDynamoDbEntry(ref Amazon.DynamoDBv2.DocumentModel.Document doc)
|
||||
{
|
||||
return doc.setAttribObject<TAttrib>(m_attrib);
|
||||
}
|
||||
|
||||
public bool onFillupJsonStringToJObject(ref JObject jobject)
|
||||
{
|
||||
return jobject.setAttribObjectWithJsonString<TAttrib>(m_attrib);
|
||||
}
|
||||
}
|
||||
275
ServerBase/DB/DynamoDb/DynamoDbClient.cs
Normal file
275
ServerBase/DB/DynamoDb/DynamoDbClient.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using Amazon.Runtime;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
|
||||
|
||||
using DYNAMO_DB_TABLE_NAME = System.String;
|
||||
using DYNAMO_DB_TABLE_FULL_NAME = System.String;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public partial class DynamoDbClient : DynamoDbConnectorBase, IModule, IInitializer
|
||||
{
|
||||
|
||||
public const string TTL_NAME = "TTL";
|
||||
public const string SK_EMPTY = "empty";
|
||||
public const string PK_GLOBAL = "global";
|
||||
|
||||
private readonly ModuleContext? m_module_context;
|
||||
|
||||
|
||||
public DynamoDbClient()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
public DynamoDbClient(ModuleContext moduleContext)
|
||||
: base()
|
||||
{
|
||||
m_module_context = moduleContext;
|
||||
}
|
||||
|
||||
public async Task<Result> onInit()
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
var result = new Result();
|
||||
|
||||
var module_context = getModuleContext();
|
||||
var config_param = module_context.getConfigParam() as ConfigParam;
|
||||
NullReferenceCheckHelper.throwIfNull(config_param, () => $"config_param is null !!!");
|
||||
|
||||
result = connectToDb(config_param);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to create DB Table !!! - {toBasicString()}";
|
||||
Log.getLogger().fatal(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (false == await createDBIfNotExists(false))
|
||||
{
|
||||
err_msg = $"Failed to createDBIfNotExists() !!! - {toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbTableCreateFailed, err_msg);
|
||||
Log.getLogger().fatal(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> startModule()
|
||||
{
|
||||
var result = new Result();
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public async Task<Result> stopModule()
|
||||
{
|
||||
var result = new Result();
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public Result connectToDb(ConfigParam configParam)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
(var error_code, var to_load_table_names) = ServerConfigHelper.getDynamoDbTableNamesWithServiceType(configParam.ServiceType);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
err_msg = $"Failed to DynamoDbClient.getDynamoDbTableNameWithServiceType() !!! : {configParam.toBasicString()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return connectToDb( to_load_table_names
|
||||
, configParam.IsLocalDynamoDB, configParam.UrlOfDynamoDb
|
||||
, configParam.AccessKeyOfAws, configParam.SecretKeyOfAws, configParam.RegionOfAws );
|
||||
}
|
||||
|
||||
public Result connectToDb( ConcurrentDictionary<DYNAMO_DB_TABLE_NAME, DYNAMO_DB_TABLE_FULL_NAME> tableNames, bool localDynamoDb
|
||||
, string dynamodbUrl
|
||||
, string awsAccessKey, string awsSecretKey
|
||||
, string awsRegion = "" )
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
foreach( var each in tableNames )
|
||||
{
|
||||
var table_name = each.Key;
|
||||
var table_full_name = each.Value;
|
||||
|
||||
if(false == addTableFullName(table_name, table_full_name))
|
||||
{
|
||||
err_msg = $"Failed to addTableFullName() !!! : tableName:{table_name}, tableFullName:{table_full_name} - {toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbTableNameDuplicated, err_msg);
|
||||
Log.getLogger().fatal(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
var db_config = new AmazonDynamoDBConfig();
|
||||
db_config.BufferSize = 4 * 1024 * 1024; // 4 MB
|
||||
db_config.RetryMode = RequestRetryMode.Standard;
|
||||
db_config.DisableLogging = false; // Logging is disabled by default. - Set to true to enable request-level logging.
|
||||
db_config.ThrottleRetries = false; // Throttled requests are not automatically retried. - Set to false to automatically retry throttled requests.
|
||||
|
||||
if (localDynamoDb)
|
||||
{
|
||||
// DynamoDB-Local is running, so create a client
|
||||
Log.getLogger().info($"Setting up a DynamoDB-Local client (DynamoDB Local seems to be running)");
|
||||
|
||||
db_config.ServiceURL = dynamodbUrl;
|
||||
|
||||
if (false == base.connectToDb(awsAccessKey, awsSecretKey, db_config))
|
||||
{
|
||||
err_msg = $"Failed to connect a DynamoDB-Local client !!! : {toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbConnectFailed, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.getLogger().info($"Setting up a DynamoDB-Remote client : TargetRegion:{awsRegion}");
|
||||
|
||||
try
|
||||
{
|
||||
db_config.RegionEndpoint = Amazon.RegionEndpoint.GetBySystemName(awsRegion);
|
||||
|
||||
if (false == base.connectToDb(awsAccessKey, awsSecretKey, db_config))
|
||||
{
|
||||
err_msg = $"Failed to connect a DynamoDB-Remote client !!! : {toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbConnectFailed, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Failed to connect a DynamoDB-Remote client !!! : Exception:{e}, {toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbConnectFailed, err_msg);
|
||||
Log.getLogger().fatal(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Result connectToDb( string tableName, bool localDynamoDB, string dynamodbUrl
|
||||
, string awsAccessKey, string awsSecretKey, string awsRegion = "")
|
||||
{
|
||||
var table_names = new ConcurrentDictionary<DYNAMO_DB_TABLE_NAME, DYNAMO_DB_TABLE_FULL_NAME>();
|
||||
table_names[tableName] = tableName;
|
||||
|
||||
return connectToDb( table_names, localDynamoDB, dynamodbUrl
|
||||
, awsAccessKey, awsSecretKey, awsRegion );
|
||||
}
|
||||
|
||||
public async Task<bool> createDBIfNotExists(bool resetTable)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var db_client = getDbClient();
|
||||
if (db_client == null)
|
||||
{
|
||||
err_msg = $"Not created DynamoDbClient !!! - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
var loaded_table_full_names = getTableNames().Values.ToList();
|
||||
|
||||
if (resetTable == true)
|
||||
{
|
||||
if(false == await deleteTables(loaded_table_full_names))
|
||||
{
|
||||
err_msg = $"Failed to DynamoDbClient.deleteTables() !!! - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List<AttributeDefinition> tableAttributes = new List<AttributeDefinition>();
|
||||
tableAttributes.Add(new AttributeDefinition(PrimaryKey.PK_Define, ScalarAttributeType.S));
|
||||
tableAttributes.Add(new AttributeDefinition(PrimaryKey.SK_Define, ScalarAttributeType.S));
|
||||
|
||||
List<KeySchemaElement> tableKeySchema = new List<KeySchemaElement>();
|
||||
tableKeySchema.Add(new KeySchemaElement(PrimaryKey.PK_Define, KeyType.HASH));
|
||||
tableKeySchema.Add(new KeySchemaElement(PrimaryKey.SK_Define, KeyType.RANGE));
|
||||
|
||||
foreach (var table_full_name in loaded_table_full_names)
|
||||
{
|
||||
if (await isExistTable(table_full_name) == false)
|
||||
{
|
||||
var ttl_update = new UpdateTimeToLiveRequest
|
||||
{
|
||||
TableName = table_full_name,
|
||||
TimeToLiveSpecification = new TimeToLiveSpecification
|
||||
{
|
||||
AttributeName = TTL_NAME, // TTL로 사용할 속성 이름
|
||||
Enabled = true // TTL 사용 여부
|
||||
}
|
||||
};
|
||||
if(false == await createTable( db_client, table_full_name, ttl_update
|
||||
, tableAttributes, tableKeySchema ))
|
||||
{
|
||||
err_msg = $"Failed to DynamoDbClient.addTable() !!! - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(false == addTable(table_full_name, Table.LoadTable(db_client, table_full_name)))
|
||||
{
|
||||
err_msg = $"Failed to DynamoDbClient.addTable() !!! : tableName:{table_full_name} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DynamoDbQueryOperationOptionConfig.configure();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Table getTableByDoc<TDoc>()
|
||||
where TDoc : DynamoDbDocBase, new()
|
||||
{
|
||||
return getTableByName((new TDoc()).TableName);
|
||||
}
|
||||
|
||||
public Table getTableByDoc<TDoc>(TDoc doc)
|
||||
where TDoc : DynamoDbDocBase
|
||||
{
|
||||
return getTableByName(doc.TableName);
|
||||
}
|
||||
|
||||
public ModuleContext getModuleContext()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(m_module_context, () => $"m_module_context is null !!!");
|
||||
return m_module_context;
|
||||
}
|
||||
}
|
||||
217
ServerBase/DB/DynamoDb/DynamoDbCommon.cs
Normal file
217
ServerBase/DB/DynamoDb/DynamoDbCommon.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
using LINK_PKSK = System.String;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
/*=============================================================================================
|
||||
DynamoDb 정보 관리 흐름도
|
||||
|
||||
# 읽기는 Document Only, 쓰기는 Document + ItemRequest 로 처리는 하는 것이 적절하다 !!!
|
||||
|
||||
1. Document Only 시퀀스
|
||||
|
||||
* 읽기 => 쓰기
|
||||
DB => Document => Attrib => Doc => EntityAttribute => Doc => Attrib => Document => DB
|
||||
|
||||
2. ItemRequest Only 시퀀스
|
||||
|
||||
* 읽기 => 쓰기
|
||||
DB => ItemRequest => EntityAttribute => ItemRequest => DB
|
||||
|
||||
3. Document + ItemRequest 시퀀스
|
||||
|
||||
* 읽기 => 쓰기
|
||||
DB => ItemRequest => Document => Attrib => Doc => Attrib => Document => ItemRequest => DB
|
||||
|
||||
- kangms
|
||||
//===========================================================================================*/
|
||||
|
||||
|
||||
//=============================================================================================
|
||||
// DynamoDB의 쿼리의 종류를 정의 한다.
|
||||
//
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public enum QueryType
|
||||
{
|
||||
None = 0,
|
||||
|
||||
// 일반 쿼리
|
||||
Select, // 조회 한다.
|
||||
Insert, // 존재하지 않은 경우만 추가 한다.
|
||||
Update, // 존재하는 경우만 업데이트 한다.
|
||||
Upsert, // 존재하지 않으면 추가하고, 있으면 업데이트 한다.
|
||||
Delete, // 존재하는 경우 삭제 한다.
|
||||
|
||||
// 특수 쿼리
|
||||
ExistsDelete, // 존재하지 않을 경우, 삭제되기 전 모든 속성을 반환 한다.
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DynamoDB의 PrimaryKey를 설정하는 클래스 이다.
|
||||
//
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public class PrimaryKey
|
||||
{
|
||||
public const string PK_Define = "PK";
|
||||
public const string SK_Define = "SK";
|
||||
|
||||
[JsonProperty(PK_Define)]
|
||||
public string PK { get; private set; } = string.Empty;
|
||||
[JsonProperty(SK_Define)]
|
||||
public string SK { get; private set; } = string.Empty;
|
||||
|
||||
public PrimaryKey()
|
||||
{ }
|
||||
|
||||
public PrimaryKey(string partitionKey)
|
||||
{
|
||||
PK = partitionKey;
|
||||
}
|
||||
public PrimaryKey(string partitionKey, string sortKey)
|
||||
{
|
||||
PK = partitionKey;
|
||||
SK = sortKey;
|
||||
}
|
||||
|
||||
public bool isFilledPK()
|
||||
{
|
||||
if (true == PK.isNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool isFilledSK()
|
||||
{
|
||||
if (true == SK.isNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void fillUpPrimaryKey(string partitionKey, string sortKey)
|
||||
{
|
||||
PK = partitionKey;
|
||||
SK = sortKey;
|
||||
}
|
||||
public void fillUpPK(string partitionKey)
|
||||
{
|
||||
PK = partitionKey;
|
||||
}
|
||||
public void fillUpSK(string sortKey)
|
||||
{
|
||||
SK = sortKey;
|
||||
}
|
||||
|
||||
public void setEmptySK()
|
||||
{
|
||||
SK = DynamoDbClient.SK_EMPTY;
|
||||
}
|
||||
|
||||
public string toPKSK()
|
||||
{
|
||||
return $"{PK_Define}:{PK}-{SK_Define}:{SK})";
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// LINK_PKSK : "PK-SK"
|
||||
//=====================================================================================
|
||||
public static (Result, PrimaryKey?) parseLinkPKSK(LINK_PKSK linkPKSK)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var pksk_parts = linkPKSK.Split('-');
|
||||
if (pksk_parts.Length != 2)
|
||||
{
|
||||
err_msg = $"Invalid length of LinkPKSK !!! : 2 == length:{pksk_parts.Length}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocLinkPkSkInvalid, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, new PrimaryKey(pksk_parts[0], pksk_parts[1]));
|
||||
}
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"PrimaryKey({PK_Define}:{PK}, {SK_Define}:{SK})";
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DynamoDB의 Document Model에서 쿼리할때 옵션을 설정하는 클래스 이다.
|
||||
//
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public static class DynamoDbQueryOperationOptionConfig
|
||||
{
|
||||
private static GetItemOperationConfig m_get_item_config = new();
|
||||
|
||||
private static TransactWriteItemOperationConfig m_transact_write_config_for_exist_primary_key = new();
|
||||
private static TransactWriteItemOperationConfig m_transact_write_config_for_not_exist_primary_key = new ();
|
||||
|
||||
// Select 쿼리후 반환되는 필드 선택 설정
|
||||
private static TransactGetItemOperationConfig m_transact_get_config = new();
|
||||
public static void configure()
|
||||
{
|
||||
|
||||
{
|
||||
m_get_item_config.ConsistentRead = true;
|
||||
}
|
||||
|
||||
{
|
||||
var expression = new Amazon.DynamoDBv2.DocumentModel.Expression();
|
||||
expression.ExpressionStatement = $"attribute_exists(#{PrimaryKey.PK_Define}) AND attribute_exists(#{PrimaryKey.SK_Define})";
|
||||
expression.ExpressionAttributeNames[$"#{PrimaryKey.PK_Define}"] = PrimaryKey.PK_Define;
|
||||
expression.ExpressionAttributeNames[$"#{PrimaryKey.SK_Define}"] = PrimaryKey.SK_Define;
|
||||
|
||||
m_transact_write_config_for_exist_primary_key.ConditionalExpression = expression;
|
||||
m_transact_write_config_for_exist_primary_key.ReturnValuesOnConditionCheckFailure = Amazon.DynamoDBv2.DocumentModel.ReturnValuesOnConditionCheckFailure.AllOldAttributes;
|
||||
}
|
||||
|
||||
{
|
||||
var expression = new Amazon.DynamoDBv2.DocumentModel.Expression();
|
||||
expression.ExpressionStatement = $"attribute_not_exists(#{PrimaryKey.PK_Define}) AND attribute_not_exists(#{PrimaryKey.SK_Define})";
|
||||
expression.ExpressionAttributeNames[$"#{PrimaryKey.PK_Define}"] = PrimaryKey.PK_Define;
|
||||
expression.ExpressionAttributeNames[$"#{PrimaryKey.SK_Define}"] = PrimaryKey.SK_Define;
|
||||
|
||||
m_transact_write_config_for_not_exist_primary_key.ConditionalExpression = expression;
|
||||
m_transact_write_config_for_not_exist_primary_key.ReturnValuesOnConditionCheckFailure = Amazon.DynamoDBv2.DocumentModel.ReturnValuesOnConditionCheckFailure.AllOldAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
public static TransactWriteItemOperationConfig getTransactWriteConfigForExistKey() => m_transact_write_config_for_exist_primary_key;
|
||||
public static TransactWriteItemOperationConfig getTransactWriteConfigForNotExistKey() => m_transact_write_config_for_not_exist_primary_key;
|
||||
|
||||
public static GetItemOperationConfig getItemConfig() => m_get_item_config;
|
||||
}
|
||||
59
ServerBase/DB/DynamoDb/DynamoDbConfig.cs
Normal file
59
ServerBase/DB/DynamoDb/DynamoDbConfig.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public partial class DynamoDbClient
|
||||
{
|
||||
public class ConfigParam : IConfigParam
|
||||
{
|
||||
public string ServiceType { get; set; } = string.Empty;
|
||||
public bool IsLocalDynamoDB { get; set; } = false;
|
||||
public string UrlOfDynamoDb { get; set; } = string.Empty;
|
||||
public string AccessKeyOfAws { get; set; } = string.Empty;
|
||||
public string SecretKeyOfAws { get; set; } = string.Empty;
|
||||
public string RegionOfAws { get; set; } = string.Empty;
|
||||
|
||||
public Result tryReadFromJsonOrDefault(JObject jObject)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
ServiceType = jObject["ServiceType"]?.Value<string>() ?? ServiceType;
|
||||
UrlOfDynamoDb = jObject["Dynamodb"]?.Value<string>() ?? UrlOfDynamoDb;
|
||||
var aws = jObject["AWS"];
|
||||
NullReferenceCheckHelper.throwIfNull(aws, () => $"aws is null !!!");
|
||||
IsLocalDynamoDB = aws["LocalDynamoDB"]?.Value<bool>() ?? IsLocalDynamoDB;
|
||||
AccessKeyOfAws = aws["AccessKey"]?.Value<string>() ?? AccessKeyOfAws;
|
||||
SecretKeyOfAws = aws["SecretKey"]?.Value<string>() ?? SecretKeyOfAws;
|
||||
RegionOfAws = aws["Region"]?.Value<string>() ?? RegionOfAws;
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var error_code = ServerErrorCode.TryCatchException;
|
||||
err_msg = $"Exception !!!, Failed to perform in tryReadFromJsonOrDefault() !!! : errorCode:{error_code}, exception:{e} - {this.getTypeName()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"ConfigParam: ServiceType:{ServiceType}, UrlOfDynamoDb:{UrlOfDynamoDb}, isLocalDynamoDb:{IsLocalDynamoDB}, RegionOfAWS:{RegionOfAws}";
|
||||
}
|
||||
};
|
||||
}
|
||||
766
ServerBase/DB/DynamoDb/DynamoDbDocBase.cs
Normal file
766
ServerBase/DB/DynamoDb/DynamoDbDocBase.cs
Normal file
@@ -0,0 +1,766 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Globalization;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using Amazon.Runtime;
|
||||
using Microsoft.AspNetCore.Server.IIS.Core;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
|
||||
using DYNAMO_DB_TABLE_FULL_NAME = System.String;
|
||||
using DYNAMO_DB_TABLE_NAME = System.String;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//=============================================================================================
|
||||
// DynamoDb Document 기반 시간 설정
|
||||
//
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public class DbTimestamp
|
||||
{
|
||||
public bool IsSet { get; set; } = false;
|
||||
|
||||
public DateTime ProcessedTime { get; set; } = DateTimeHelper.MinTime;
|
||||
|
||||
public void setProcessedTime(DateTime currentTime)
|
||||
{
|
||||
ProcessedTime = currentTime;
|
||||
IsSet = true;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
ProcessedTime = DateTimeHelper.MinTime;
|
||||
IsSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DynamoDb Document 기반 Query Context
|
||||
//
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public partial class DynamoDbDocumentQueryContext : IQueryContext
|
||||
{
|
||||
private readonly DYNAMO_DB_TABLE_FULL_NAME m_table_full_name;
|
||||
|
||||
private readonly Document m_document;
|
||||
private QueryType m_query_type;
|
||||
|
||||
// TransactionCanceledException 관련 설정
|
||||
private DynamoDbQueryExceptionNotifier.ExceptionHandler? m_exception_handler_nullable;
|
||||
|
||||
|
||||
public DynamoDbDocumentQueryContext( DYNAMO_DB_TABLE_FULL_NAME tableFullName
|
||||
, Document document, QueryType queryType )
|
||||
{
|
||||
m_table_full_name = tableFullName;
|
||||
|
||||
m_document = document;
|
||||
m_query_type = queryType;
|
||||
}
|
||||
|
||||
public DynamoDbDocumentQueryContext( DYNAMO_DB_TABLE_FULL_NAME tableFullName
|
||||
, Document document, QueryType queryType
|
||||
, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler )
|
||||
{
|
||||
m_table_full_name = tableFullName;
|
||||
|
||||
m_document = document;
|
||||
m_query_type = queryType;
|
||||
|
||||
m_exception_handler_nullable = exceptionHandler;
|
||||
}
|
||||
|
||||
public bool isValid()
|
||||
{
|
||||
if (true == m_table_full_name.isNullOrWhiteSpace())
|
||||
{
|
||||
Log.getLogger().error($"TableFullName is Null or WhiteSpace !!! - {toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null == m_document)
|
||||
{
|
||||
Log.getLogger().error($"DynamoDbDocument is null !!! - {toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QueryType.None == m_query_type)
|
||||
{
|
||||
Log.getLogger().error($"DynamoDbDocument QueryType invalid !!! - {toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public DYNAMO_DB_TABLE_FULL_NAME getTableFullName()
|
||||
{
|
||||
ConditionValidCheckHelper.throwIfFalseWithCondition(() => false == m_table_full_name.isNullOrWhiteSpace(), () => $"Invalid TableFullName !!!, Null of WhiteSpace !!! - {m_document.toPKSK()}");
|
||||
return m_table_full_name;
|
||||
}
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()}, TableFullName:{m_table_full_name}, {m_query_type}, {m_document?.toBasicString()}";
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// PK(Partition Key) : Primary Key 내의 Partition Key + 해당 정보의 Domain 식별키
|
||||
// SK(Sort Key) : Primary Key 내의 Sort Key, 해당 정보의 고유 식별키
|
||||
// DocType : DynamoDbDocBase 상속 객체명
|
||||
// Attrib ... : AttribBase 상속 객체명
|
||||
// CreatedDateTime : Item Document 생성 시간
|
||||
// UpdateedDateTime : Item Document 마지막 업데이트 시간 (생성, 삭제, 복구시 동일 시간으로 반영)
|
||||
// DeletedDateTime : Item Document 삭제 시간
|
||||
// RestoredDateTime : Item Document 복구 시간
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public abstract partial class DynamoDbDocBase : IRowData
|
||||
{
|
||||
// 관계된 Table Name, 특화된 테이블명이 있다면 재정의해야 한다. - kangms
|
||||
public virtual DYNAMO_DB_TABLE_NAME TableName
|
||||
{
|
||||
get {
|
||||
return DynamoDbDefine.TableNames.Main;
|
||||
}
|
||||
}
|
||||
|
||||
public const string CreatedDateTime = "CreatedDateTime";
|
||||
public const string UpdatedDateTime = "UpdatedDateTime";
|
||||
public const string DeletedDateTime = "DeletedDateTime";
|
||||
public const string RestoredDateTime = "RestoredDateTime";
|
||||
|
||||
private PrimaryKey m_key = new();
|
||||
|
||||
// DocType은 DynamoDb Document 정보를 Read시 식별키로 사용되기 때문에 고유하게 정의되어야 한다 !!! - kangms
|
||||
[JsonProperty("DocType")]
|
||||
private readonly string m_doc_type;
|
||||
|
||||
public const string ATTRIBUTE_PATH = "attribute_path";
|
||||
|
||||
private ConcurrentDictionary<System.Type, IAttribWrapper> m_attrib_wrappers = new();
|
||||
|
||||
private DbTimestamp m_created_datetime = new();
|
||||
private DbTimestamp m_updated_datetime = new();
|
||||
private DbTimestamp m_deleted_datetime = new();
|
||||
private DbTimestamp m_restored_datetime = new();
|
||||
|
||||
private string m_combination_key_for_pk = string.Empty;
|
||||
private string m_combination_key_for_sk = string.Empty;
|
||||
|
||||
private Result m_initialize_result = new();
|
||||
|
||||
private bool m_is_enable_ttl = false;
|
||||
private Int64 m_ttl_seconds = default;
|
||||
|
||||
private QueryType m_query_type = QueryType.None;
|
||||
|
||||
private DynamoDbQueryExceptionNotifier.ExceptionHandler? m_exception_handler_nullable;
|
||||
|
||||
public DynamoDbDocBase(string docType)
|
||||
{
|
||||
m_doc_type = docType;
|
||||
}
|
||||
|
||||
// 필독 !!!
|
||||
// 최초 등록시 TTL 설정을 적용해야 한다.
|
||||
// TTL 설정이 적용되면 TTL 시간이 다시 적용됨을 유의 !!! - kangms
|
||||
public DynamoDbDocBase(string docType, Int64 ttlSeconds)
|
||||
{
|
||||
m_doc_type = docType;
|
||||
|
||||
enableTTL(ttlSeconds);
|
||||
}
|
||||
|
||||
public void SetTtlSeconds(Int64 ttlSeconds)
|
||||
{
|
||||
m_ttl_seconds = ttlSeconds;
|
||||
}
|
||||
|
||||
public bool isNotSetupQueryType()
|
||||
{
|
||||
if(QueryType.None == m_query_type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool appendAttribWrapper(IAttribWrapper wrapper)
|
||||
{
|
||||
var result = getInitializeResult();
|
||||
|
||||
if (false == m_attrib_wrappers.TryAdd(wrapper.getAttribType(), wrapper))
|
||||
{
|
||||
var error_code = ServerErrorCode.DynamoDbDocAttribTypeDuplicated;
|
||||
var err_msg = $"Failed to TryAdd AttribWrapper !!!, duplicated AttribType : AttribType:{wrapper.getAttribType()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
|
||||
Log.getLogger().error(err_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual async Task<Result> newDoc4Query()
|
||||
{
|
||||
var result = getInitializeResult();
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await onPrepareCRUD();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var current_time = DateTimeHelper.Current;
|
||||
setCreatedDateTime(current_time);
|
||||
changeUpdatedDateTime(current_time);
|
||||
|
||||
m_query_type = QueryType.Insert;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual async Task<Result> updateDoc4Query()
|
||||
{
|
||||
var result = getInitializeResult();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await onPrepareCRUD();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
changeUpdatedDateTime(DateTimeHelper.Current);
|
||||
|
||||
m_query_type = QueryType.Update;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual async Task<Result> upsertDoc4Query()
|
||||
{
|
||||
var result = getInitializeResult();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await onPrepareCRUD();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var current_time = DateTimeHelper.Current;
|
||||
setCreatedDateTime(current_time);
|
||||
changeUpdatedDateTime(current_time);
|
||||
|
||||
m_query_type = QueryType.Upsert;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual async Task<Result> deleteDoc4Query()
|
||||
{
|
||||
var result = getInitializeResult();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await onPrepareCRUD();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
setDeletedDateTime(DateTimeHelper.Current);
|
||||
|
||||
m_query_type = QueryType.Delete;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual async Task<Result> onCopyFromDocument(Amazon.DynamoDBv2.DocumentModel.Document fromDocument)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var doc_type = fromDocument.getDocType();
|
||||
var pk = fromDocument.getPK();
|
||||
var sk = fromDocument.getSK();
|
||||
|
||||
try
|
||||
{
|
||||
result = getInitializeResult();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var error_code = fillUpPrimaryKey(pk, sk);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
err_msg = $"Failed to fillUpPrimaryKey() !!! : {error_code}";
|
||||
result.setFail(error_code, err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var ttl_name = DynamoDbClient.TTL_NAME;
|
||||
if (fromDocument.Contains(ttl_name))
|
||||
{
|
||||
fromDocument[ttl_name].AsString();
|
||||
}
|
||||
|
||||
if (false == fillUpDocType(doc_type))
|
||||
{
|
||||
err_msg = $"Failed to fillUpDocType() !!! : docType:{doc_type} - PK:{pk}, SK:{sk}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocTypeNotMatch, err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var each in m_attrib_wrappers)
|
||||
{
|
||||
var attrib_wrapper = each.Value;
|
||||
NullReferenceCheckHelper.throwIfNull(attrib_wrapper, () => $"attrib_wrapper is null !!! - docType:{doc_type}, PK:{pk}, SK:{sk}");
|
||||
|
||||
if (false == attrib_wrapper.onCopyFromDynamoDbEntry(fromDocument))
|
||||
{
|
||||
err_msg = $"Failed to onCopyFromDynamoDbEntry() !!! : attribType:{attrib_wrapper.getAttribType()} - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocAttribWrapperCopyFailed, err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (false == fromDocument.getTimestampByDB_TIMESTAMP(DynamoDbDocBase.CreatedDateTime, out var created_time))
|
||||
{
|
||||
err_msg = $"Failed to getTimestampByDB_TIMESTAMP() !!!, CreatedDateTime - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
setCreatedDateTime(created_time);
|
||||
}
|
||||
|
||||
if (false == fromDocument.getTimestampByDB_TIMESTAMP(DynamoDbDocBase.UpdatedDateTime, out var updated_time))
|
||||
{
|
||||
err_msg = $"Failed to getTimestampByDB_TIMESTAMP() !!!, UpdatedDateTime - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
setUpdatedDateTime(updated_time);
|
||||
}
|
||||
|
||||
if (false == fromDocument.getTimestampByDB_TIMESTAMP(DynamoDbDocBase.DeletedDateTime, out var deleteded_time))
|
||||
{
|
||||
err_msg = $"Failed to getTimestampByDB_TIMESTAMP() !!!, DeletedDateTime - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
setDeletedDateTime(deleteded_time);
|
||||
}
|
||||
|
||||
if (false == fromDocument.getTimestampByDB_TIMESTAMP(DynamoDbDocBase.RestoredDateTime, out var restored_time))
|
||||
{
|
||||
err_msg = $"Failed to getTimestampByDB_TIMESTAMP() !!!, RestoredDateTime - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
setRestoredDateTime(restored_time);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Exception !!!, Failed to perform in DynamoDbDocBase.onCopyFromDocument() !!! : exception:{e} - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocCopyFailedFromDocument, err_msg);
|
||||
|
||||
Log.getLogger().fatal(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public virtual async Task<(Result, Amazon.DynamoDBv2.DocumentModel.Document)> onCopyToDocument()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var toDocument = new Amazon.DynamoDBv2.DocumentModel.Document();
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var doc_type = getDocType();
|
||||
var pk = onMakePK();
|
||||
var sk = onMakeSK();
|
||||
|
||||
try
|
||||
{
|
||||
result = getInitializeResult();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, toDocument);
|
||||
}
|
||||
|
||||
var error_code = fillUpPrimaryKey(onMakePK(), onMakeSK());
|
||||
if (error_code.isFail())
|
||||
{
|
||||
err_msg = $"Failed to fillUpPrimaryKey() !!! : {error_code}";
|
||||
result.setFail(error_code, err_msg);
|
||||
|
||||
return (result, toDocument);
|
||||
}
|
||||
|
||||
if (false == fillUpDocType(getDocType()))
|
||||
{
|
||||
return (result, toDocument);
|
||||
}
|
||||
|
||||
toDocument[PrimaryKey.PK_Define] = getPK();
|
||||
toDocument[PrimaryKey.SK_Define] = getSK();
|
||||
if (isEnableTTL())
|
||||
{
|
||||
toDocument[DynamoDbClient.TTL_NAME] = getTTLSeconds();
|
||||
}
|
||||
toDocument["DocType"] = getDocType();
|
||||
|
||||
foreach (var each in m_attrib_wrappers)
|
||||
{
|
||||
each.Value.onCopyToDynamoDbEntry(ref toDocument);
|
||||
}
|
||||
|
||||
if(true == getCreatedDateTime().IsSet)
|
||||
{
|
||||
toDocument[DynamoDbDocBase.CreatedDateTime] = getCreatedDateTime().ProcessedTime;
|
||||
}
|
||||
if (true == getUpdatedDateTime().IsSet)
|
||||
{
|
||||
toDocument[DynamoDbDocBase.UpdatedDateTime] = getUpdatedDateTime().ProcessedTime;
|
||||
}
|
||||
if (true == getDeletedDateTime().IsSet)
|
||||
{
|
||||
toDocument[DynamoDbDocBase.DeletedDateTime] = getDeletedDateTime().ProcessedTime;
|
||||
}
|
||||
if (true == getRestoredDateTime().IsSet)
|
||||
{
|
||||
toDocument[DynamoDbDocBase.RestoredDateTime] = getRestoredDateTime().ProcessedTime;
|
||||
}
|
||||
|
||||
return (result, toDocument);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
err_msg = $"Exception !!!, Failed to perform in DynamoDbDocBase.onCopyToDocument() !!! : exception:{e} - docType:{doc_type}, PK:{pk}, SK:{sk}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocCopyFailedToDocument, err_msg);
|
||||
Log.getLogger().fatal(err_msg);
|
||||
|
||||
return (result, toDocument);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void copyTimestampsFromOriginDocBase(DynamoDbDocBase originDocBase)
|
||||
{
|
||||
setCreatedDateTime(originDocBase.getCreatedDateTime().ProcessedTime);
|
||||
setUpdatedDateTime(originDocBase.getUpdatedDateTime().ProcessedTime);
|
||||
setDeletedDateTime(originDocBase.getDeletedDateTime().ProcessedTime);
|
||||
setRestoredDateTime(originDocBase.getRestoredDateTime().ProcessedTime);
|
||||
}
|
||||
|
||||
protected abstract string onGetPrefixOfPK();
|
||||
|
||||
protected virtual string onMakePK()
|
||||
{
|
||||
return $"{onGetPrefixOfPK()}{getCombinationKeyForPK()}";
|
||||
}
|
||||
|
||||
protected virtual string onGetPrefixOfSK() { return ""; }
|
||||
|
||||
protected virtual string onMakeSK() { return getCombinationKeyForSK(); }
|
||||
|
||||
protected ServerErrorCode fillUpPrimaryKey(string partitionKey, string sortKey)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var error_code = onCheckAndSetPK(partitionKey);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
return error_code;
|
||||
}
|
||||
|
||||
if(0 < sortKey.Length)
|
||||
{
|
||||
if(true == sortKey.isEmptySK())
|
||||
{
|
||||
getPrimaryKey().setEmptySK();
|
||||
}
|
||||
else
|
||||
{
|
||||
error_code = onCheckAndSetSK(sortKey);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
err_msg = $"Failed to onCheckAndSetSK() !!!, param sortKey exist : sorkKey:{sortKey}, {error_code.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return error_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
protected virtual ServerErrorCode onCheckAndSetPK(string partitionKey)
|
||||
{
|
||||
var error_code = onFillupCombinationKeyForPK(partitionKey, out var combination_key_for_pk);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
return error_code;
|
||||
}
|
||||
|
||||
getPrimaryKey().fillUpPK(partitionKey);
|
||||
setCombinationKeyForPK(combination_key_for_pk);
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
public virtual ServerErrorCode onFillupCombinationKeyForPK(string partitionKey, out string fillupKey)
|
||||
{
|
||||
fillupKey = string.Empty;
|
||||
|
||||
var prefix_of_pk = onGetPrefixOfPK();
|
||||
if (true == prefix_of_pk.isNullOrWhiteSpace())
|
||||
{
|
||||
fillupKey = partitionKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
var combination_key_for_pk = partitionKey.Replace(onGetPrefixOfPK(), "");
|
||||
if (0 >= combination_key_for_pk.Length)
|
||||
{
|
||||
var err_msg = $"PK combination 0 is length !!! : PrimaryKey:{partitionKey} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
|
||||
return ServerErrorCode.DynamoDbDocPkInvalid;
|
||||
}
|
||||
|
||||
fillupKey = combination_key_for_pk;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
protected virtual ServerErrorCode onCheckAndSetSK(string sortKey)
|
||||
{
|
||||
return ServerErrorCode.FunctionNotImplemented;
|
||||
}
|
||||
|
||||
public virtual ServerErrorCode onFillupCombinationKeyForSK(string sortKey, out string fillupKey)
|
||||
{
|
||||
fillupKey = string.Empty;
|
||||
|
||||
var prefix_of_sk = onGetPrefixOfSK();
|
||||
if(true == prefix_of_sk.isNullOrWhiteSpace())
|
||||
{
|
||||
fillupKey = sortKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
var combination_key_for_sk = sortKey.Replace(prefix_of_sk, "");
|
||||
if (0 >= combination_key_for_sk.Length)
|
||||
{
|
||||
var err_msg = $"SK combination 0 is length !!! : SortKey:{sortKey} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
|
||||
return ServerErrorCode.DynamoDbDocSkInvalid;
|
||||
}
|
||||
|
||||
fillupKey = combination_key_for_sk;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
public virtual ServerErrorCode onApplyPKSK()
|
||||
{
|
||||
return fillUpPrimaryKey(onMakePK(), onMakeSK());
|
||||
}
|
||||
|
||||
protected Int64 getTTLSeconds() => m_ttl_seconds;
|
||||
|
||||
protected virtual Task<Result> onPrepareCRUD()
|
||||
{
|
||||
return Task.FromResult(new Result());
|
||||
}
|
||||
|
||||
private bool fillUpDocType(string docType)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if ( false == m_doc_type.Equals(docType, StringComparison.OrdinalIgnoreCase) )
|
||||
{
|
||||
err_msg = $"DocType Not matched !!! : currDocType:{m_doc_type} == reqDocType:{docType} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public TAttrib? getAttrib<TAttrib>()
|
||||
where TAttrib : AttribBase
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var to_find_attrib_type = typeof(TAttrib);
|
||||
if (false == m_attrib_wrappers.TryGetValue(to_find_attrib_type, out var lookup_attrib))
|
||||
{
|
||||
err_msg = $"Not found AttribType !!! : toFindAttribType:{to_find_attrib_type.Name} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
var found_attrib = lookup_attrib.getAttribBase() as TAttrib;
|
||||
if (null == found_attrib)
|
||||
{
|
||||
err_msg = $"Failed to cast AttribType : toCastAttribType:{to_find_attrib_type.Name} == lookupAttribType:{lookup_attrib.getAttribType().Name} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
return found_attrib;
|
||||
}
|
||||
|
||||
protected void setCreatedDateTime(DateTime currentTime)
|
||||
{
|
||||
m_created_datetime.setProcessedTime(currentTime);
|
||||
}
|
||||
|
||||
protected void setUpdatedDateTime(DateTime currentTime)
|
||||
{
|
||||
m_updated_datetime.setProcessedTime(currentTime);
|
||||
}
|
||||
|
||||
protected void changeUpdatedDateTime(DateTime currentTime)
|
||||
{
|
||||
m_updated_datetime.setProcessedTime(currentTime);
|
||||
}
|
||||
|
||||
protected void setDeletedDateTime(DateTime currentTime)
|
||||
{
|
||||
m_deleted_datetime.setProcessedTime(currentTime);
|
||||
}
|
||||
|
||||
protected void setRestoredDateTime(DateTime currentTime)
|
||||
{
|
||||
m_restored_datetime.setProcessedTime(currentTime);
|
||||
}
|
||||
|
||||
public virtual string toAttribAllString()
|
||||
{
|
||||
var attrib_string = string.Empty;
|
||||
|
||||
foreach(var attrib_wrapper in m_attrib_wrappers.Values)
|
||||
{
|
||||
attrib_string += attrib_wrapper.getAttribBase().toBasicString();
|
||||
attrib_string += ", ";
|
||||
}
|
||||
|
||||
var found_index = attrib_string.LastIndexOf(", ");
|
||||
if(found_index != -1)
|
||||
{
|
||||
attrib_string.Remove(found_index);
|
||||
}
|
||||
|
||||
return attrib_string;
|
||||
}
|
||||
|
||||
protected bool isEnableTTL() => m_is_enable_ttl;
|
||||
|
||||
public void desableTTL()
|
||||
{
|
||||
m_ttl_seconds = 0;
|
||||
m_is_enable_ttl = false;
|
||||
}
|
||||
|
||||
public void enableTTL(long ttlSeconds)
|
||||
{
|
||||
m_ttl_seconds = ttlSeconds;
|
||||
m_is_enable_ttl = true;
|
||||
}
|
||||
|
||||
|
||||
public string toDocTypePKSK()
|
||||
{
|
||||
return $"{this.getTypeName()}:{getPrimaryKey().toBasicString()}";
|
||||
}
|
||||
|
||||
public string toJsonStringOfAttribs()
|
||||
{
|
||||
var json_object = new JObject();
|
||||
foreach (var each in m_attrib_wrappers)
|
||||
{
|
||||
if(false == each.Value.onFillupJsonStringToJObject(ref json_object))
|
||||
{
|
||||
var err_msg = $"Failed to onFillupJsonStringToDynamoDbEntry() !!! : {each.Value.getAttribType().Name} - {toBasicString()}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(json_object, Formatting.Indented);
|
||||
}
|
||||
|
||||
public virtual string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()}: tableName:{TableName}, {getPrimaryKey().toBasicString()}"
|
||||
+ $", {getCreatedDateTime().ProcessedTime.toString("o")}"
|
||||
+ $", {getUpdatedDateTime().ProcessedTime.toString("o")}"
|
||||
+ $", {getDeletedDateTime().ProcessedTime.toString("o")}"
|
||||
+ $", {getRestoredDateTime().ProcessedTime.toString("o")}";
|
||||
}
|
||||
}
|
||||
66
ServerBase/DB/DynamoDb/DynamoDbItemRequestBase.cs
Normal file
66
ServerBase/DB/DynamoDb/DynamoDbItemRequestBase.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//=============================================================================================
|
||||
// DynamoDb ItemRequest 기반 Query Context
|
||||
//
|
||||
// - kangms
|
||||
//=============================================================================================
|
||||
|
||||
public partial class DynamoDbItemRequestQueryContext : IQueryContext
|
||||
{
|
||||
private readonly AmazonDynamoDBRequest m_db_request;
|
||||
private QueryType m_query_type;
|
||||
|
||||
// TransactionCanceledException 관련 설정
|
||||
private DynamoDbQueryExceptionNotifier.ExceptionHandler? m_exception_handler_nullable;
|
||||
|
||||
|
||||
public DynamoDbItemRequestQueryContext( AmazonDynamoDBRequest dbRequest, QueryType queryType )
|
||||
{
|
||||
m_db_request = dbRequest;
|
||||
m_query_type = queryType;
|
||||
}
|
||||
|
||||
public DynamoDbItemRequestQueryContext( AmazonDynamoDBRequest dbRequest, QueryType queryType
|
||||
, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler )
|
||||
{
|
||||
m_db_request = dbRequest;
|
||||
m_query_type = queryType;
|
||||
m_exception_handler_nullable = exceptionHandler;
|
||||
}
|
||||
|
||||
public bool isValid()
|
||||
{
|
||||
if (null == m_db_request)
|
||||
{
|
||||
Log.getLogger().error($"DynamoDbDocument is null !!! - {toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QueryType.None == m_query_type)
|
||||
{
|
||||
Log.getLogger().error($"DynamoDbDocument QueryType invalid !!! - {toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()}, {m_query_type}, {m_db_request?.toBasicString()}";
|
||||
}
|
||||
}
|
||||
77
ServerBase/DB/DynamoDb/DynamoDbQueryExceptionNotifier.cs
Normal file
77
ServerBase/DB/DynamoDb/DynamoDbQueryExceptionNotifier.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public class DynamoDbQueryExceptionNotifier
|
||||
{
|
||||
public const string Success = "None"; // 쿼리 성공
|
||||
public const string ConditionalCheckFailed = "ConditionalCheckFailed"; // 쿼리 실패 : 조건들이 충족하지 못해 실패 했다.
|
||||
public const string ItemCollectionSizeLimitExceeded = "ItemCollectionSizeLimitExceeded"; // 쿼리 실패 : 아이템 제한 개수 초과로 실패 했다.
|
||||
|
||||
public class ExceptionHandler
|
||||
{
|
||||
public ExceptionHandler(string reasonCode, ServerErrorCode returnToErrorCode)
|
||||
{
|
||||
ReasonCode = reasonCode;
|
||||
ReturnToErrorCode = returnToErrorCode;
|
||||
}
|
||||
|
||||
public bool hasReasonCode(string reasonCode)
|
||||
{
|
||||
return ReasonCode.Equals(reasonCode, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string ReasonCode { get; set; }
|
||||
|
||||
public ServerErrorCode ReturnToErrorCode { get; set; }
|
||||
}
|
||||
|
||||
private Int32 m_query_seq_no = 0;
|
||||
private readonly Dictionary<Int32, ExceptionHandler> m_exception_handlers = new();
|
||||
|
||||
public DynamoDbQueryExceptionNotifier()
|
||||
{
|
||||
}
|
||||
|
||||
public bool registerExceptionHandler(ExceptionHandler? exceptionHandler)
|
||||
{
|
||||
m_query_seq_no++;
|
||||
|
||||
if(null == exceptionHandler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(true == m_exception_handlers.ContainsKey(m_query_seq_no))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_exception_handlers[m_query_seq_no] = exceptionHandler;
|
||||
return true;
|
||||
}
|
||||
|
||||
public ExceptionHandler? findExceptionHandler(Int32 querySeqNo, string reasonCode)
|
||||
{
|
||||
if(false == m_exception_handlers.TryGetValue(querySeqNo, out var exception_handler))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if(false == exception_handler.hasReasonCode(reasonCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return exception_handler;
|
||||
}
|
||||
}
|
||||
61
ServerBase/DB/MongoDb/MongoDbConfig.cs
Normal file
61
ServerBase/DB/MongoDb/MongoDbConfig.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public partial class MongoDbConnector : MongoDbConnectorBase, IModule, IInitializer
|
||||
{
|
||||
public class ConfigParam : IConfigParam
|
||||
{
|
||||
public string ServiceType { get; set; } = string.Empty;
|
||||
public string DatabaseName { get; set; } = string.Empty;
|
||||
public string ConnectionString { get; set; } = string.Empty;
|
||||
public int MinConnectionPoolSize { get; set; } = 0;
|
||||
public int MaxConnectionPoolSize { get; set; } = 100;
|
||||
public int WaitQueueTimeoutSecs { get; set; } = 120;
|
||||
|
||||
public Result tryReadFromJsonOrDefault(JObject jObject)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
ServiceType = jObject["ServiceType"]?.Value<string>() ?? ServiceType;
|
||||
|
||||
var mongo_db = jObject["MongoDb"];
|
||||
NullReferenceCheckHelper.throwIfNull(mongo_db, () => $"mongo_db is null !!!");
|
||||
ConnectionString = mongo_db["ConnectionString"]?.Value<string>() ?? ConnectionString;
|
||||
DatabaseName = mongo_db["DatabaseName"]?.Value<string>() ?? DatabaseName;
|
||||
MinConnectionPoolSize = mongo_db["MinConnectionPoolSize"]?.Value<int>() ?? MinConnectionPoolSize;
|
||||
MaxConnectionPoolSize = mongo_db["MaxConnectionPoolSize"]?.Value<int>() ?? MaxConnectionPoolSize;
|
||||
WaitQueueTimeoutSecs = mongo_db["WaitQueueTimeoutSecs"]?.Value<int>() ?? WaitQueueTimeoutSecs;
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var error_code = ServerErrorCode.TryCatchException;
|
||||
err_msg = $"Exception !!!, Failed to perform in tryReadFromJsonOrDefault() !!! : errorCode:{error_code}, exception:{e} - {this.getTypeName()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"ConfigParam: ServiceType:{ServiceType}, ConnectionStringOfMongoDb:{ConnectionString}, MinConnectionPoolSize:{MinConnectionPoolSize}, MaxConnectionPoolSize:{MaxConnectionPoolSize}, WaitQueueTimeoutSecs:{WaitQueueTimeoutSecs}";
|
||||
}
|
||||
};
|
||||
}
|
||||
62
ServerBase/DB/MongoDb/MongoDbConnector.cs
Normal file
62
ServerBase/DB/MongoDb/MongoDbConnector.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using MongoDB.Driver;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public partial class MongoDbConnector : MongoDbConnectorBase, IModule, IInitializer
|
||||
{
|
||||
private readonly ModuleContext m_module_context;
|
||||
|
||||
public MongoDbConnector(ModuleContext moduleContext)
|
||||
: base()
|
||||
{
|
||||
m_module_context = moduleContext;
|
||||
}
|
||||
|
||||
public async Task<Result> onInit()
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
var result = new Result();
|
||||
|
||||
var module_context = getModuleContext();
|
||||
var config_param = module_context.getConfigParam() as ConfigParam;
|
||||
NullReferenceCheckHelper.throwIfNull(config_param, () => $"config_param is null !!!");
|
||||
|
||||
(var is_success, var db_list) = await initAndVerifyDb( config_param.ConnectionString
|
||||
, config_param.MinConnectionPoolSize
|
||||
, config_param.MaxConnectionPoolSize
|
||||
, config_param.WaitQueueTimeoutSecs );
|
||||
if(false == is_success)
|
||||
{
|
||||
err_msg = $"Failed to initAndVerifyDb() !!! : {config_param.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.MongoDbInitAndVefifyDbFailed, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> startModule()
|
||||
{
|
||||
var result = new Result();
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public async Task<Result> stopModule()
|
||||
{
|
||||
var result = new Result();
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
|
||||
public ModuleContext getModuleContext() => m_module_context;
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"{m_module_context.toBasicString()} - {this.getTypeName()}";
|
||||
}
|
||||
}
|
||||
213
ServerBase/DB/MySqlDb/MySqlDbConnector.cs
Normal file
213
ServerBase/DB/MySqlDb/MySqlDbConnector.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
|
||||
|
||||
using MySqlConnector;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
public partial class MySqlDbConnector : IDisposable
|
||||
{
|
||||
// MySql 접속 재시도 간격 시간 밀리초)
|
||||
private Int32 MYSQL_CONNECT_RETRY_INTERVAL_MSEC = 3000;
|
||||
// MySql 접속 재시도 간격 활성화 조건 실패 횟수
|
||||
private Int32 MYSQL_CONNECT_RETRY_INTERVAL_ENABLE_CONDITION_FAILED_COUNT = 3;
|
||||
// private Int16 m_curr_retry_connect_count = 0;
|
||||
|
||||
private string m_connection_string = string.Empty;
|
||||
|
||||
private MySqlConnection? m_connection;
|
||||
private ConnectionState m_last_connection_state = ConnectionState.Closed;
|
||||
|
||||
private delegate int MyDelegate(int a, int b);
|
||||
|
||||
public MySqlDbConnector()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public async Task<ServerErrorCode> initMySql(string connectionString)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var error_code = createConnection(connectionString);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
return error_code;
|
||||
}
|
||||
|
||||
error_code = await openMySql();
|
||||
if (error_code.isFail())
|
||||
{
|
||||
Log.getLogger().error($"Failed to initMySql() !!! : errorCode:{error_code} - connectString:{m_connection_string}");
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
public ServerErrorCode createConnection(string connectionString)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(null != m_connection)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
m_connection_string = connectionString;
|
||||
m_connection = new MySqlConnection(connectionString);
|
||||
|
||||
// 연결 상태 변경에 대한 이벤트를 받기 위해 등록 한다. - kangms
|
||||
m_connection.StateChange += new StateChangeEventHandler(onStateChange);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, new MySqlConnection() !!! : message:{e}, connectString:{m_connection_string}");
|
||||
return ServerErrorCode.MySqlConnectionCreateFailed;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
public async Task<ServerErrorCode> openMySql()
|
||||
{
|
||||
if (m_connection == null)
|
||||
{
|
||||
return ServerErrorCode.MySqlConnectionCreateFailed;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await m_connection.OpenAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.getLogger().error($"Exception to open MySqlConnection !!! : errDesc:{e.Message} - connectString:{m_connection_string}");
|
||||
return ServerErrorCode.MySqlConnectionOpenFailed;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// 객체 소멸시점 MySqlConnection 이 ConnectionPool 에 반환될 수 있도록
|
||||
// MySqlConnection.Dispose() 를 반드시 호출해 주어야 한다. - kangms
|
||||
if (m_connection != null)
|
||||
{
|
||||
m_connection.Dispose();
|
||||
m_connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void disposeMySql()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
//=========================================================================================
|
||||
// MySqlDbConnector.onStateChange() 함수가 호출되는 시점은
|
||||
// 1. Self Disconnect : MySqlConnection.Dispose() 이 호출되는 시점 (Using 으로 생성후 Using 범위를 벗어나는 순간, 즉 GC 호출 시점)
|
||||
// 2. Other Disconnect : 실제로 MySql 서버와 끊어진 경우
|
||||
// 3. 기타 : 비정상적인 상황
|
||||
//=========================================================================================
|
||||
private void onStateChange(object sender, StateChangeEventArgs e)
|
||||
{
|
||||
if (m_connection == null)
|
||||
{
|
||||
Log.getLogger().error($"Failed to onStateChange(), Connection is null or CurrentState is null !!! - connectString:{m_connection_string}");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.CurrentState)
|
||||
{
|
||||
case ConnectionState.Closed:
|
||||
Log.getLogger().debug($"Closed MySqlConnector !!! - connectString:{m_connection_string}");
|
||||
break;
|
||||
|
||||
case ConnectionState.Open:
|
||||
Log.getLogger().debug($"Opened MySqlConnector !!! - connectString:{m_connection_string}");
|
||||
break;
|
||||
}
|
||||
|
||||
m_last_connection_state = e.CurrentState;
|
||||
}
|
||||
|
||||
public async Task<ServerErrorCode> retryOpenMySql()
|
||||
{
|
||||
Log.getLogger().info($"Retry db connect start !!! - connectString:{m_connection_string}");
|
||||
|
||||
var error_code = ServerErrorCode.Success;
|
||||
|
||||
for (var i = 0; i < MYSQL_CONNECT_RETRY_INTERVAL_ENABLE_CONDITION_FAILED_COUNT; i++)
|
||||
{
|
||||
await Task.Delay(MYSQL_CONNECT_RETRY_INTERVAL_MSEC);
|
||||
|
||||
error_code = createConnection(m_connection_string);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
Log.getLogger().error($"Failed to createConnection() when retryOpenMySql() !!! : errorCode:{error_code}, retryCount:{i} - connectString:{m_connection_string}");
|
||||
continue;
|
||||
}
|
||||
|
||||
error_code = await openMySql();
|
||||
if (error_code.isFail())
|
||||
{
|
||||
Log.getLogger().error($"Failed to openMySql() when retryOpenMySql() !!! : errorCode:{error_code}, retryCount:{i} - connectString:{m_connection_string}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.getLogger().info($"Success Retry db connect !!! : retryCount:{i} - connectString:{m_connection_string}");
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
||||
public async Task<ServerErrorCode> querySQL(string queryString, Func<MySqlDataReader, ServerErrorCode> dataReader)
|
||||
{
|
||||
var error_code = ServerErrorCode.Success;
|
||||
|
||||
try
|
||||
{
|
||||
using (var cmd = new MySqlCommand(queryString, m_connection))
|
||||
using (var reader = await cmd.ExecuteReaderAsync())
|
||||
{
|
||||
if (true == await reader.ReadAsync())
|
||||
{
|
||||
if (null != dataReader)
|
||||
{
|
||||
error_code = dataReader.Invoke(reader);
|
||||
if (error_code.isFail())
|
||||
{
|
||||
Log.getLogger().error($"Failed to dataReader.Invoke() when querySQL() !!! : errorCode:{error_code}, queryString:{queryString} - connectString:{m_connection_string}");
|
||||
return error_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
error_code = ServerErrorCode.MySqlDbQueryException;
|
||||
var err_msg = $"Exception !!!, Failed to query for Read when querySQL() !!! : errorCode:{error_code}, exception:{e}, queryString:{queryString} - connectString:{m_connection_string}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return error_code;
|
||||
}
|
||||
|
||||
return error_code;
|
||||
}
|
||||
}
|
||||
610
ServerBase/DB/QueryBatch.cs
Normal file
610
ServerBase/DB/QueryBatch.cs
Normal file
@@ -0,0 +1,610 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
using NLog;
|
||||
using Amazon.Runtime;
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using Amazon.DynamoDBv2.DataModel;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
using LOG_ACTION_TYPE = System.String;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//==============================================================================================
|
||||
// 배치 쿼리를 전담 처리해 주는 추상 클래스 이다.
|
||||
//==============================================================================================
|
||||
|
||||
public abstract partial class QueryBatchBase
|
||||
{
|
||||
public enum QueryResultType
|
||||
{
|
||||
Success = 0, // 쿼리를 성공 했다.
|
||||
|
||||
NotCalledQueryFunc = 1, // 쿼리 함수가 호출되지 않았다.
|
||||
CalledQueryFunc = 2, // 쿼리 함수를 호출 했다.
|
||||
QueryFailed = 3, // 쿼리를 실패 했다.
|
||||
}
|
||||
|
||||
private DynamoDbClient m_dynamo_db_connector;
|
||||
|
||||
private IQueryRunner m_query_runner;
|
||||
|
||||
private bool m_is_use_transact = false;
|
||||
private readonly string m_trans_id = string.Empty;
|
||||
|
||||
private UInt16 m_retry_delay_msec = 0;
|
||||
private UInt16 m_retry_count = 0;
|
||||
|
||||
private readonly List<QueryExecutorBase> m_querys = new();
|
||||
|
||||
private IWithLogActor m_log_actor;
|
||||
private readonly LogAction m_log_action;
|
||||
private readonly List<ILogInvoker> m_business_logs = new();
|
||||
|
||||
public QueryBatchBase( IWithLogActor logActor, LOG_ACTION_TYPE logActionType
|
||||
, DynamoDbClient dbConnector
|
||||
, IQueryRunner queryRunner
|
||||
, bool isUseTransact = true, string transId = ""
|
||||
, UInt16 retryCount = 0, UInt16 retryDelayMSec = 0)
|
||||
{
|
||||
m_log_actor = logActor;
|
||||
m_dynamo_db_connector = dbConnector;
|
||||
m_query_runner = queryRunner;
|
||||
|
||||
m_is_use_transact = isUseTransact;
|
||||
m_trans_id = string.IsNullOrEmpty(transId) ? Guid.NewGuid().ToString("N") : transId;
|
||||
|
||||
m_log_action = new LogAction(logActionType);
|
||||
|
||||
m_retry_count = retryCount;
|
||||
m_retry_delay_msec = retryDelayMSec;
|
||||
}
|
||||
|
||||
public QueryBatchBase( IWithLogActor logActor, LogAction logAction
|
||||
, DynamoDbClient dbConnector
|
||||
, IQueryRunner queryRunner
|
||||
, bool isUseTransact = true, string transId = ""
|
||||
, UInt16 retryCount = 0, UInt16 retryDelayMSec = 0)
|
||||
{
|
||||
m_log_actor = logActor;
|
||||
m_dynamo_db_connector = dbConnector;
|
||||
m_query_runner = queryRunner;
|
||||
|
||||
m_is_use_transact = isUseTransact;
|
||||
m_trans_id = string.IsNullOrEmpty(transId) ? Guid.NewGuid().ToString("N") : transId;
|
||||
|
||||
m_log_action = logAction;
|
||||
|
||||
m_retry_count = retryCount;
|
||||
m_retry_delay_msec = retryDelayMSec;
|
||||
}
|
||||
|
||||
public bool addQuery( QueryExecutorBase queryContext
|
||||
, QueryExecutorBase.FnCommit? fnCommit = null
|
||||
, QueryExecutorBase.FnRollback? fnRollback = null )
|
||||
{
|
||||
queryContext.bindCallback(fnCommit, fnRollback);
|
||||
queryContext.setQueryBatch(this);
|
||||
|
||||
if(false == queryContext.onAddQueryRequests())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_querys.Add(queryContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================================
|
||||
// DB 쿼리 주요 실행 함수들...
|
||||
//=========================================================================================
|
||||
|
||||
public async Task<Result> prepareQueryWithStopwatch()
|
||||
{
|
||||
Stopwatch? stopwatch = null;
|
||||
var event_tid = string.Empty;
|
||||
|
||||
var server_logic = ServerLogicApp.getServerLogicApp();
|
||||
ArgumentNullException.ThrowIfNull(server_logic);
|
||||
var server_config = server_logic.getServerConfig();
|
||||
ArgumentNullException.ThrowIfNull(server_config);
|
||||
|
||||
if (true == server_config.PerformanceCheckEnable)
|
||||
{
|
||||
event_tid = string.IsNullOrEmpty(getTransId()) ? System.Guid.NewGuid().ToString("N") : getTransId();
|
||||
stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
var result = await prepareQuery();
|
||||
|
||||
if (null != stopwatch)
|
||||
{
|
||||
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
||||
stopwatch.Stop();
|
||||
|
||||
if (elapsed_msec > Constant.STOPWATCH_LOG_LIMIT_MSEC)
|
||||
{
|
||||
Log.getLogger().debug($"QueryBatch - prepareQuery Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<Result> prepareQuery()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
foreach(var query in m_querys)
|
||||
{
|
||||
result = await query.onPrepareQuery();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> doQueryWithStopwatch()
|
||||
{
|
||||
Stopwatch? stopwatch = null;
|
||||
var event_tid = string.Empty;
|
||||
|
||||
var server_logic = ServerLogicApp.getServerLogicApp();
|
||||
ArgumentNullException.ThrowIfNull(server_logic);
|
||||
var server_config = server_logic.getServerConfig();
|
||||
ArgumentNullException.ThrowIfNull(server_config);
|
||||
|
||||
if (true == server_config.PerformanceCheckEnable)
|
||||
{
|
||||
event_tid = string.IsNullOrEmpty(getTransId()) ? System.Guid.NewGuid().ToString("N") : getTransId();
|
||||
stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
var result = await doQuery();
|
||||
|
||||
if (null != stopwatch)
|
||||
{
|
||||
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
||||
stopwatch.Stop();
|
||||
|
||||
if (elapsed_msec > Constant.STOPWATCH_LOG_LIMIT_MSEC)
|
||||
{
|
||||
Log.getLogger().debug($"QueryBatch - doQuery Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<Result> doQuery()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var query in m_querys)
|
||||
{
|
||||
result = await query.onQuery();
|
||||
if (result.isFail())
|
||||
{
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
err_msg = $"Exception !!!, doQuery(), Exception:{e} - {toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbQueryException, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (result.isSuccess())
|
||||
{
|
||||
result = await commitQueryAllWithStopWatch();
|
||||
}
|
||||
else
|
||||
{
|
||||
await rollbackQueryAll(result);
|
||||
}
|
||||
|
||||
onSendMsgByHandler(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// 1. 등록된 쿼리들을 등록 순서대로 처리해 준다.
|
||||
// 2. Non-Transaction 쿼리에 오류가 발생할 경우 이후 처리할 쿼리는 모두 실행하지 않는다.
|
||||
// 3. TransactionGetItem 쿼리에 오류가 발생할 경우 TransactionGetItemRequest에
|
||||
// 등록된 모든 쿼리는 처리되지 않는다.
|
||||
// 4. TransactionWriteItem 쿼리에 오류가 발생할 경우 TransactionWriteItemRequest에
|
||||
// 등록된 모든 쿼리는 처리되지 않는다.
|
||||
//=====================================================================================
|
||||
|
||||
private async Task<Result> commitQueryAllWithStopWatch()
|
||||
{
|
||||
Stopwatch? stopwatch = null;
|
||||
var event_tid = string.Empty;
|
||||
|
||||
var server_logic = ServerLogicApp.getServerLogicApp();
|
||||
ArgumentNullException.ThrowIfNull(server_logic);
|
||||
var server_config = server_logic.getServerConfig();
|
||||
ArgumentNullException.ThrowIfNull(server_config);
|
||||
|
||||
if (true == server_config.PerformanceCheckEnable)
|
||||
{
|
||||
event_tid = string.IsNullOrEmpty(getTransId()) ? System.Guid.NewGuid().ToString("N") : getTransId();
|
||||
stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
var result = await commitQueryAll();
|
||||
|
||||
if (null != stopwatch)
|
||||
{
|
||||
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
||||
stopwatch.Stop();
|
||||
|
||||
if (elapsed_msec > 100)
|
||||
{
|
||||
Log.getLogger().debug($"QueryBatch - commitQueryAll Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<Result> commitQueryAll()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var try_count = 0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
(result, bool is_retry_query) = await commitQueryRunner();
|
||||
if (true == is_retry_query)
|
||||
{
|
||||
try_count++;
|
||||
if (m_retry_count <= try_count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(m_retry_delay_msec);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isFail())
|
||||
{
|
||||
await rollbackQueryAll(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var query in m_querys)
|
||||
{
|
||||
if (QueryBatchBase.QueryResultType.NotCalledQueryFunc == await query.doFnCommit())
|
||||
{
|
||||
await query.onQueryResponseCommit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<(Result, bool)> commitQueryRunner()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
result = await onCommitQueryRunner();
|
||||
}
|
||||
catch (TransactionCanceledException e)
|
||||
{
|
||||
var error_code = ServerErrorCode.DynamoDbTransactionCanceledException;
|
||||
err_msg = $"Failed to commitQueryRunner() !!!, Transaction Canceled, implies a client issue, fix before retrying !!! : errorCode:{error_code}, {e.toExceptionString()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
(var dispatch_result, var exception_custom_result) = await onDispatchTransactionCancelException(e);
|
||||
if (dispatch_result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to onDispatchTransactionCancelException() !!! : {dispatch_result.toBasicString()}, {e.toExceptionString()} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 재정의된 오류 코드가 설정된 경우
|
||||
if (exception_custom_result.isFail())
|
||||
{
|
||||
// 재정의한 예외 결과를 dispatch 했다면 오류 로그로 설정해 준다. !!!
|
||||
err_msg = $"QueryBatch.onDispatchTransactionCancelException() !!! : exceptionResult:{exception_custom_result.getResultString()} - {toBasicString()}";
|
||||
result.setFail(exception_custom_result.getErrorCode(), exception_custom_result.getResultString());
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TransactionConflictException e)
|
||||
{
|
||||
var error_code = ServerErrorCode.DynamoDbTransactionConflictException;
|
||||
err_msg = $"TransactionConflictException !!!, Failed to perform in commitQueryRunner() !!!, Transaction conflict occurred !!! : errorCode:{error_code}, {e.toExceptionString()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
}
|
||||
catch (AmazonDynamoDBException e)
|
||||
{
|
||||
var error_code = ServerErrorCode.DynamoDbAmazonDynamoDbException;
|
||||
err_msg = $"AmazonDynamoDBException !!!, Failed to perform in commitQueryRunner() !!! : errorCode:{error_code}, {e.toExceptionString()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
catch (AmazonServiceException e)
|
||||
{
|
||||
var error_code = ServerErrorCode.DynamoDbAmazonServiceException;
|
||||
err_msg = $"AmazonServiceException !!!, Failed to perform in commitQueryRunner() !!! : errorCode:{error_code}, {e.toExceptionString()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return (result, true);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
var error_code = ServerErrorCode.TryCatchException;
|
||||
err_msg = $"Exception !!!, Failed to perform in commitQueryRunner() !!! : errorCode:{error_code}, exception:{e} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
}
|
||||
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
private async Task rollbackQueryAll(Result errorResult)
|
||||
{
|
||||
await onRollbackQueryRunnner();
|
||||
|
||||
foreach (var query in m_querys)
|
||||
{
|
||||
if (QueryBatchBase.QueryResultType.NotCalledQueryFunc == await query.doFnRollback(errorResult))
|
||||
{
|
||||
await query.onQueryResponseRollback(errorResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Task<Result> onCommitQueryRunner();
|
||||
|
||||
protected abstract Task onRollbackQueryRunnner();
|
||||
|
||||
public virtual void onSendMsgByHandler(Result errorResult)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// 배치쿼리중에서 특정 쿼리를 찾는다.
|
||||
//==============================================================================================
|
||||
public TQuery? getQuery<TQuery>()
|
||||
where TQuery : QueryExecutorBase
|
||||
{
|
||||
return m_querys.Find(x => x is TQuery == true) as TQuery;
|
||||
}
|
||||
|
||||
public List<TQuery> getQuerys<TQuery>()
|
||||
where TQuery : QueryExecutorBase
|
||||
{
|
||||
var querys = new List<TQuery>();
|
||||
foreach (var each in m_querys)
|
||||
{
|
||||
if (each is TQuery query)
|
||||
{
|
||||
querys.Add(query);
|
||||
}
|
||||
}
|
||||
|
||||
return querys;
|
||||
}
|
||||
|
||||
public void appendBusinessLogs(List<ILogInvoker> logs)
|
||||
{
|
||||
foreach(var log in logs)
|
||||
{
|
||||
m_business_logs.Add(log);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendBusinessLog(ILogInvoker log)
|
||||
{
|
||||
m_business_logs.Add(log);
|
||||
}
|
||||
|
||||
private bool hasBusinessLog()
|
||||
{
|
||||
var log_actor = getLogActor();
|
||||
|
||||
if (null != log_actor && 0 < m_business_logs.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void sendBusinessLog()
|
||||
{
|
||||
if (false == hasBusinessLog())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BusinessLogger.collectLogs(m_log_action, m_log_actor, m_business_logs);
|
||||
}
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()}, LogActionType:{m_log_action.getLogActionType()}, TransId:{m_trans_id}";
|
||||
}
|
||||
|
||||
protected abstract Task<(Result, Result)> onDispatchTransactionCancelException(TransactionCanceledException exception);
|
||||
}//QueryBatchBase
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// 배치 쿼리를 전담 처리해 주는 제네릭 클래스 이다.
|
||||
//==============================================================================================
|
||||
public partial class QueryBatch<TQueryRunner> : QueryBatchBase
|
||||
where TQueryRunner : IQueryRunner, new()
|
||||
{
|
||||
public QueryBatch( IWithLogActor logActor
|
||||
, LOG_ACTION_TYPE logActionType
|
||||
, DynamoDbClient dbClient
|
||||
, bool isUseTransact = true, string transId = ""
|
||||
, UInt16 retryCount = 3, UInt16 retryDelayMSec = 1000, string eventTid = "" )
|
||||
: base( logActor, logActionType, dbClient, new TQueryRunner()
|
||||
, isUseTransact, transId, retryCount, retryDelayMSec)
|
||||
{
|
||||
getQueryRunner().setQueryBatch(this);
|
||||
}
|
||||
|
||||
public QueryBatch( IWithLogActor logActor
|
||||
, LogAction logAction
|
||||
, DynamoDbClient dbClient
|
||||
, bool isUseTransact = true, string transId = ""
|
||||
, UInt16 retryCount = 3, UInt16 retryDelayMSec = 1000, string eventTid = "" )
|
||||
: base( logActor, logAction.getLogActionType(), dbClient, new TQueryRunner()
|
||||
, isUseTransact, transId, retryCount, retryDelayMSec)
|
||||
{
|
||||
getQueryRunner().setQueryBatch(this);
|
||||
}
|
||||
|
||||
protected override async Task<Result> onCommitQueryRunner()
|
||||
{
|
||||
var query_runner = getQueryRunner();
|
||||
var result = await query_runner.onTryCommitQueryRunner();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return await getQueryRunner().onCommitQueryRunner();
|
||||
}
|
||||
|
||||
protected override async Task onRollbackQueryRunnner()
|
||||
{
|
||||
await getQueryRunner().onRollbackQueryRunner();
|
||||
}
|
||||
|
||||
protected override async Task<(Result, Result)> onDispatchTransactionCancelException(TransactionCanceledException exception)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var log_actor = getLogActor();
|
||||
ArgumentNullException.ThrowIfNull(log_actor, $"log_actor is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(exception, $"exception is null !!! - {log_actor.getTypeName()}");
|
||||
|
||||
return await getQueryRunner().onHandleTransactionCancelException(exception);
|
||||
}
|
||||
}//QueryBatch<TQueryRunner>
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// 배치 쿼리 + 모든 쿼리 처리후 TMsg 를 결과로 전송해 주는 제네릭 클래스 이다.
|
||||
//==============================================================================================
|
||||
public class QueryBatch<TQueryRunner, TMsg> : QueryBatch<TQueryRunner>
|
||||
where TQueryRunner : IQueryRunner, new()
|
||||
where TMsg : class, new()
|
||||
{
|
||||
protected readonly TMsg m_reserved_msg ; // 전송하고자 하는 패킷
|
||||
protected readonly object m_sender; // 전송객체 : setError(ErrorCode error), sendPacketAsync(TMsg msg) 맴버함수가 있어야 한다. !!!
|
||||
|
||||
public QueryBatch( IWithLogActor logActor
|
||||
, LOG_ACTION_TYPE logActionType
|
||||
, DynamoDbClient dbClient
|
||||
, object sender
|
||||
, TMsg? msg = null
|
||||
, bool isUseTransact = true )
|
||||
: base(logActor, logActionType, dbClient, isUseTransact )
|
||||
{
|
||||
if (msg == null)
|
||||
{
|
||||
m_reserved_msg = new TMsg();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reserved_msg = msg;
|
||||
}
|
||||
|
||||
m_sender = sender;
|
||||
}
|
||||
|
||||
public QueryBatch( IWithLogActor logActor
|
||||
, LogAction logAction
|
||||
, DynamoDbClient dbClient
|
||||
, object sender
|
||||
, TMsg? msg = null
|
||||
, bool isUseTransact = true )
|
||||
: base(logActor, logAction.getLogActionType(), dbClient, isUseTransact )
|
||||
{
|
||||
if (msg == null)
|
||||
{
|
||||
m_reserved_msg = new TMsg();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reserved_msg = msg;
|
||||
}
|
||||
|
||||
m_sender = sender;
|
||||
}
|
||||
|
||||
public TMsg Msg
|
||||
{
|
||||
get { return m_reserved_msg; }
|
||||
}
|
||||
|
||||
private void setErrorToMsg(Result errorResult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void onSendMsgByHandler(Result errorResult)
|
||||
{
|
||||
if (m_reserved_msg != null && m_sender != null)
|
||||
{
|
||||
setErrorToMsg(errorResult);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}//QueryBatch<TQueryRunner, TMsg>
|
||||
254
ServerBase/DB/QueryDbRequester.cs
Normal file
254
ServerBase/DB/QueryDbRequester.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//==============================================================================================
|
||||
// DB 쿼리 종류별 재정의 클래스들 이다.
|
||||
//==============================================================================================
|
||||
|
||||
public partial class DBRequestGetBatch : QueryDbRequesterWithItemRequestBase<BatchGetItemRequest>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var db_client = query_batch.getDynamoDbConnector().getDbClient();
|
||||
ArgumentNullException.ThrowIfNull(db_client, $"db_client is null !!!");
|
||||
|
||||
//=====================================================================================
|
||||
// Batch 기반 DB Get Request 처리 : Non-Transaction
|
||||
//=====================================================================================
|
||||
var response = await db_client.BatchGetItemAsync(getDbRequestGenericType());
|
||||
ArgumentNullException.ThrowIfNull(response, $"response is null !!!");
|
||||
|
||||
await response.logDetail();
|
||||
|
||||
query_runner.addDbResponse(response);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestWriteBatch : QueryDbRequesterWithItemRequestBase<BatchWriteItemRequest>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var db_client = query_batch.getDynamoDbConnector().getDbClient();
|
||||
ArgumentNullException.ThrowIfNull(db_client, $"db_client is null !!!");
|
||||
|
||||
//=====================================================================================
|
||||
// Batch 기반 DB Write Request 처리 : Non-Transaction
|
||||
//=====================================================================================
|
||||
var response = await db_client.BatchWriteItemAsync(getDbRequestGenericType());
|
||||
ArgumentNullException.ThrowIfNull(response, $"response is null !!!");
|
||||
|
||||
await response.logDetail();
|
||||
|
||||
query_runner.addDbResponse(response);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestUpdate : QueryDbRequesterWithItemRequestBase<UpdateItemRequest>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var db_client = query_batch.getDynamoDbConnector().getDbClient();
|
||||
ArgumentNullException.ThrowIfNull(db_client, $"db_client is null !!!");
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
// Update 기반 DB Update Request 처리 : Non-Transaction
|
||||
//=====================================================================================
|
||||
var response = await db_client.UpdateItemAsync(getDbRequestGenericType());
|
||||
ArgumentNullException.ThrowIfNull(response, $"response is null !!!");
|
||||
|
||||
await response.logDetail();
|
||||
|
||||
query_runner.addDbResponse(response);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestGetWithTransact : QueryDbRequesterWithItemRequestBase<TransactGetItemsRequest>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var db_client = query_batch.getDynamoDbConnector().getDbClient();
|
||||
ArgumentNullException.ThrowIfNull(db_client, $"db_client is null !!!");
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
// Transaction 기반 DB Get Request 처리
|
||||
//=====================================================================================
|
||||
var response = await db_client.TransactGetItemsAsync(getDbRequestGenericType());
|
||||
ArgumentNullException.ThrowIfNull(response, $"response is null !!!");
|
||||
|
||||
await response.logDetail(getDbRequestGenericType());
|
||||
|
||||
query_runner.addDbResponse(response);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestWriteWithTransact : QueryDbRequesterWithItemRequestBase<TransactWriteItemsRequest>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var db_client = query_batch.getDynamoDbConnector().getDbClient();
|
||||
ArgumentNullException.ThrowIfNull(db_client, $"db_client is null !!!");
|
||||
|
||||
//=====================================================================================
|
||||
// Transaction 기반 DB Write Request 처리
|
||||
//=====================================================================================
|
||||
var response = await db_client.TransactWriteItemsAsync(getDbRequestGenericType());
|
||||
ArgumentNullException.ThrowIfNull(response, $"response is null !!!");
|
||||
|
||||
await response.logDetail();
|
||||
|
||||
query_runner.addDbResponse(response);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// DB 쿼리 Document 전용 재정의 클래스들 이다.
|
||||
//==============================================================================================
|
||||
|
||||
public partial class DBRequestGetWithDocumentBatchGet : QueryDbRequesterWithDocumentBatchGetBase<DocumentBatchGet>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithDocument;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var doc_batch_get = query_runner.getOrNewMultiTableDocumentBatchGet();
|
||||
ArgumentNullException.ThrowIfNull(doc_batch_get, $"doc_batch_get is null !!!");
|
||||
|
||||
doc_batch_get.AddBatch(getDbRequestGenericType());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestWriteWithDocumentBatchWrite : QueryDbRequesterWithDocumentBatchWriteBase<DocumentBatchWrite>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithDocument;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var doc_batch_write = query_runner.getOrNewMultiTableDocumentBatchWrite();
|
||||
ArgumentNullException.ThrowIfNull(doc_batch_write, $"doc_batch_write is null !!!");
|
||||
|
||||
doc_batch_write.AddBatch(getDbRequestGenericType());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestGetWithDocumentTransactGet : QueryDbRequesterWithDocumentTransactGetBase<DocumentTransactGet>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithDocument;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var doc_transact_get = query_runner.getOrNewMultiTableDocumentTransactGet();
|
||||
ArgumentNullException.ThrowIfNull(doc_transact_get, $"doc_transact_get is null !!!");
|
||||
|
||||
doc_transact_get.AddTransactionPart(getDbRequestGenericType());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DBRequestWriteWithDocumentTransactWrite : QueryDbRequesterWithDocumentTransactWriteBase<DocumentTransactWrite>
|
||||
{
|
||||
public override async Task<Result> doDbRequestAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var query_runner = query_batch.getQueryRunner() as QueryRunnerWithDocument;
|
||||
ArgumentNullException.ThrowIfNull(query_runner, $"query_runner is null !!!");
|
||||
var doc_transact_write = query_runner.getOrNewMultiTableDocumentTransactWrite();
|
||||
ArgumentNullException.ThrowIfNull(doc_transact_write, $"doc_transact_write is null !!!");
|
||||
|
||||
doc_transact_write.AddTransactionPart(getDbRequestGenericType());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
133
ServerBase/DB/QueryDbRequesterBase.cs
Normal file
133
ServerBase/DB/QueryDbRequesterBase.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리 ItemRequest 타입 구현 하기위한 추상 + 제네릭 클래스 이다.
|
||||
//=============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithItemRequestBase<TDbRequestType> : IQueryDbRequester
|
||||
where TDbRequestType : AmazonDynamoDBRequest, new()
|
||||
{
|
||||
private QueryBatchBase? m_query_batch;
|
||||
private readonly TDbRequestType m_db_request;
|
||||
|
||||
private DynamoDbQueryExceptionNotifier m_exception_notifier = new();
|
||||
|
||||
public QueryDbRequesterWithItemRequestBase()
|
||||
{
|
||||
m_db_request = new TDbRequestType();
|
||||
}
|
||||
|
||||
public abstract Task<Result> doDbRequestAsync();
|
||||
|
||||
public DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifier() => m_exception_notifier;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리 DocumentBatchWrite 타입 구현 하기위한 추상 + 제네릭 클래스 이다.
|
||||
//=============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentBatchWriteBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentBatchWrite
|
||||
{
|
||||
private QueryBatchBase? m_query_batch;
|
||||
private TDbRequestType? m_db_request;
|
||||
private Table? m_table;
|
||||
|
||||
private DynamoDbQueryExceptionNotifier m_exception_notifier = new();
|
||||
|
||||
public void onCreateDocumentModel(Table table)
|
||||
{
|
||||
m_table = table;
|
||||
|
||||
var db_request_type = table.CreateBatchWrite() as TDbRequestType;
|
||||
NullReferenceCheckHelper.throwIfNull(db_request_type, () => $"db_request_type is null !!!");
|
||||
|
||||
m_db_request = db_request_type;
|
||||
}
|
||||
|
||||
public abstract Task<Result> doDbRequestAsync();
|
||||
|
||||
public DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifier() => m_exception_notifier;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리 DocumentBatchGet 타입 구현 하기위한 추상 + 제네릭 클래스 이다.
|
||||
//=============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentBatchGetBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentBatchGet
|
||||
{
|
||||
private QueryBatchBase? m_query_batch;
|
||||
private TDbRequestType? m_db_request;
|
||||
private Table? m_table;
|
||||
|
||||
private DynamoDbQueryExceptionNotifier m_exception_notifier = new();
|
||||
|
||||
public void onCreateDocumentModel(Table table)
|
||||
{
|
||||
m_table = table;
|
||||
m_db_request = table.CreateBatchGet() as TDbRequestType;
|
||||
}
|
||||
|
||||
public abstract Task<Result> doDbRequestAsync();
|
||||
|
||||
public DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifier() => m_exception_notifier;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리 DocumentTransactWrite 타입 구현 하기위한 추상 + 제네릭 클래스 이다.
|
||||
//=============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentTransactWriteBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentTransactWrite
|
||||
{
|
||||
private QueryBatchBase? m_query_batch = null;
|
||||
private TDbRequestType? m_db_request;
|
||||
private Table? m_table;
|
||||
|
||||
private DynamoDbQueryExceptionNotifier m_exception_notifier = new();
|
||||
|
||||
public void onCreateDocumentModel(Table table)
|
||||
{
|
||||
m_table = table;
|
||||
m_db_request = table.CreateTransactWrite() as TDbRequestType;
|
||||
}
|
||||
|
||||
public abstract Task<Result> doDbRequestAsync();
|
||||
|
||||
public DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifier() => m_exception_notifier;
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리 DocumentTransactGet 타입 구현 하기위한 추상 + 제네릭 클래스 이다.
|
||||
//=============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentTransactGetBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentTransactGet
|
||||
{
|
||||
private QueryBatchBase? m_query_batch = null;
|
||||
private TDbRequestType? m_db_request;
|
||||
private Table? m_table;
|
||||
|
||||
private DynamoDbQueryExceptionNotifier m_exception_notifier = new();
|
||||
|
||||
public void onCreateDocumentModel(Table table)
|
||||
{
|
||||
m_table = table;
|
||||
m_db_request = table.CreateTransactGet() as TDbRequestType;
|
||||
}
|
||||
|
||||
public abstract Task<Result> doDbRequestAsync();
|
||||
|
||||
public DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifier() => m_exception_notifier;
|
||||
}
|
||||
189
ServerBase/DB/QueryExecutorBase.cs
Normal file
189
ServerBase/DB/QueryExecutorBase.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//==============================================================================================
|
||||
// 쿼리 내용을 작성하는 기반 클래스
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryExecutorBase
|
||||
{
|
||||
private readonly string m_query_name;
|
||||
|
||||
public delegate Task<QueryBatchBase.QueryResultType> FnCommit(QueryExecutorBase query);
|
||||
public delegate Task<QueryBatchBase.QueryResultType> FnRollback(QueryExecutorBase query, Result errorResult);
|
||||
|
||||
private FnCommit? m_fn_commit; // 쿼리 성공시 호출될 콜백함수
|
||||
private FnRollback? m_fn_rollback; // 쿼리 실패시 호출될 콜백함수
|
||||
|
||||
private QueryBatchBase? m_parent; // 배치쿼리
|
||||
|
||||
|
||||
public QueryExecutorBase(string queryName)
|
||||
{
|
||||
m_query_name = queryName;
|
||||
}
|
||||
|
||||
public void bindCallback(FnCommit? fnCommit, FnRollback? fnRollback)
|
||||
{
|
||||
m_fn_commit = fnCommit;
|
||||
m_fn_rollback = fnRollback;
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 작성한다.
|
||||
//=====================================================================================
|
||||
public virtual bool onAddQueryRequests() => true;
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리 직전에 준비해야 할 로직들을 작성한다.
|
||||
//=====================================================================================
|
||||
public abstract Task<Result> onPrepareQuery();
|
||||
|
||||
//=====================================================================================
|
||||
// onPrepareQuery()를 성공할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public abstract Task<Result> onQuery();
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 성공하고, doFnCommit()가 QueryResultType.NotCalledQueryFunc를 반환할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public abstract Task onQueryResponseCommit();
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 실패하고, doFnRollback()가 QueryResultType.NotCalledQueryFunc를 반환할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public abstract Task onQueryResponseRollback(Result errorResult);
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
// DB commit 성공후 호출된다.
|
||||
//=====================================================================================
|
||||
public async Task<QueryBatchBase.QueryResultType> doFnCommit()
|
||||
{
|
||||
if (null != m_fn_commit)
|
||||
{
|
||||
await m_fn_commit(this);
|
||||
return QueryBatchBase.QueryResultType.CalledQueryFunc;
|
||||
}
|
||||
|
||||
return QueryBatchBase.QueryResultType.NotCalledQueryFunc;
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// DB commit 실패후 호출된다.
|
||||
//=====================================================================================
|
||||
public async Task<QueryBatchBase.QueryResultType> doFnRollback(Result errorResult)
|
||||
{
|
||||
if (null != m_fn_rollback)
|
||||
{
|
||||
await m_fn_rollback(this, errorResult);
|
||||
return QueryBatchBase.QueryResultType.CalledQueryFunc;
|
||||
}
|
||||
|
||||
return QueryBatchBase.QueryResultType.NotCalledQueryFunc;
|
||||
}
|
||||
|
||||
public void appendBusinessLog(ILogInvoker log)
|
||||
{
|
||||
if(null == m_parent)
|
||||
{
|
||||
var err_msg = $"Failed to QueryContextBase.appendBusinessLog() !!!, m_parent is null - {log.toBasicString()}, {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent?.appendBusinessLog(log);
|
||||
}
|
||||
|
||||
public TQuery? getQuery<TQuery>()
|
||||
where TQuery : QueryExecutorBase
|
||||
{
|
||||
if (this is TQuery query)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
if (null == m_parent)
|
||||
{
|
||||
var err_msg = $"Failed to QueryContextBase.getQuery<{nameof(TQuery)}>() !!!, m_parent is null - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_parent.getQuery<TQuery>();
|
||||
}
|
||||
|
||||
public List<TQuery>? getQuerys<TQuery>()
|
||||
where TQuery : QueryExecutorBase
|
||||
{
|
||||
if (null == m_parent)
|
||||
{
|
||||
var err_msg = $"Failed to QueryContextBase.getQuerys<{nameof(TQuery)}>() !!!, m_parent is null - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_parent.getQuerys<TQuery>();
|
||||
}
|
||||
|
||||
public virtual EntityBase getOwner()
|
||||
{
|
||||
var query_batch = getQueryBatch();
|
||||
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!!");
|
||||
var owner = query_batch.getLogActor() as EntityBase;
|
||||
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
||||
return owner;
|
||||
}
|
||||
|
||||
public virtual string toBasicString()
|
||||
{
|
||||
return $"QueryName:{getQueryName()}, ";
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// 배치 쿼리시 마지막 스텝의 콜백을 받기 위한 empty 쿼리
|
||||
//==============================================================================================
|
||||
public class QueryFinal : QueryExecutorBase
|
||||
{
|
||||
|
||||
public QueryFinal()
|
||||
: base(nameof(QueryFinal))
|
||||
{ }
|
||||
|
||||
public override async Task<Result> onPrepareQuery()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override async Task<Result> onQuery()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override async Task onQueryResponseCommit()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task onQueryResponseRollback(Result errorResult)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}//QueryFinal
|
||||
158
ServerBase/DB/QueryHelper.cs
Normal file
158
ServerBase/DB/QueryHelper.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
using Amazon.Runtime;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//==============================================================================================
|
||||
// QueryBatch 클래스의 주요 절차들을 처리해 주는 클래스 이다.
|
||||
//==============================================================================================
|
||||
public static class QueryHelper
|
||||
{
|
||||
//==============================================================================================
|
||||
// 1. DB 쿼리를 요청한다.
|
||||
// 2. 배치쿼리를 요청한다.
|
||||
//==============================================================================================
|
||||
public static async Task<Result> sendQueryAndBusinessLog(QueryBatchBase batch)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if (batch == null)
|
||||
{
|
||||
err_msg = $"Failed to DBQuery !!!, QueryBatchBase is null";
|
||||
result.setFail(ServerErrorCode.FunctionInvalidParam, err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
Stopwatch? stopwatch = null;
|
||||
var event_tid = string.Empty;
|
||||
|
||||
var server_logic = ServerLogicApp.getServerLogicApp();
|
||||
ArgumentNullException.ThrowIfNull(server_logic);
|
||||
var server_config = server_logic.getServerConfig();
|
||||
ArgumentNullException.ThrowIfNull(server_config);
|
||||
|
||||
if (true == server_config.PerformanceCheckEnable)
|
||||
{
|
||||
event_tid = string.IsNullOrEmpty(batch.getTransId()) ? System.Guid.NewGuid().ToString("N") : batch.getTransId();
|
||||
stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
result = await sendQuery(batch);
|
||||
if(result.isSuccess())
|
||||
{
|
||||
batch.sendBusinessLog();
|
||||
}
|
||||
|
||||
if (null != stopwatch)
|
||||
{
|
||||
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
||||
stopwatch.Stop();
|
||||
|
||||
if (elapsed_msec > Constant.STOPWATCH_LOG_LIMIT_MSEC)
|
||||
{
|
||||
Log.getLogger().debug($"QueryBatch - Total Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, LogAction:{batch.getLogAction().getLogActionType()}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// 1. DB 쿼리를 요청한다.
|
||||
// 2. 배치쿼리를 요청한다.
|
||||
//==============================================================================================
|
||||
private static async Task<Result> sendQuery(QueryBatchBase batch)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if (batch == null)
|
||||
{
|
||||
err_msg = $"Failed to sendQuery !!!, QueryBatchBase is null";
|
||||
result.setFail(ServerErrorCode.FunctionInvalidParam, err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (false == batch.hasQuery())
|
||||
{
|
||||
err_msg = $"Not has DBQuery !!!";
|
||||
result.setFail(ServerErrorCode.DynamoDbQueryNoRequested, err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await batch.prepareQueryWithStopwatch();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await batch.doQueryWithStopwatch();
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// 쿼리 관련 확장 함수들을 정의하는 클래스 이다.
|
||||
//==============================================================================================
|
||||
|
||||
public static class QueryExtension
|
||||
{
|
||||
public static bool isSuccess(this CancellationReason reason)
|
||||
{
|
||||
return true == reason.Code.Equals(DynamoDbQueryExceptionNotifier.Success, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static string toExceptionString(this TransactionCanceledException e)
|
||||
{
|
||||
return $"[TransactionCanceledException] - exception:{e}";
|
||||
}
|
||||
|
||||
public static string toExceptionString(this AmazonDynamoDBException e)
|
||||
{
|
||||
var err_msg = $"[AmazonDynamoDBException] - occurred:{e.Message}, ";
|
||||
err_msg += (e.InnerException != null) ? $" Inner Exception:{e.InnerException.Message}," : "";
|
||||
err_msg += $" exception:{e}";
|
||||
|
||||
return err_msg;
|
||||
|
||||
}
|
||||
|
||||
public static string toExceptionString(this AmazonServiceException e)
|
||||
{
|
||||
return $"[AmazonServiceException] - exception:{e}";
|
||||
}
|
||||
|
||||
public static bool isEqualQuery<TDbRequestWithDocument>(this IQueryDbRequester dbRequester)
|
||||
where TDbRequestWithDocument : IQueryDbRequester
|
||||
{
|
||||
if(dbRequester.GetType() == typeof(TDbRequestWithDocument))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
69
ServerBase/DB/QueryInterface.cs
Normal file
69
ServerBase/DB/QueryInterface.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리를 실행해 주는 인터페이스 이다.
|
||||
//=============================================================================================
|
||||
public interface IQueryRunner
|
||||
{
|
||||
void setQueryBatch(QueryBatchBase queryBatch);
|
||||
|
||||
Task<Result> onTryCommitQueryRunner();
|
||||
Task<Result> onCommitQueryRunner();
|
||||
Task onRollbackQueryRunner();
|
||||
Task<(Result, Result)> onHandleTransactionCancelException(TransactionCanceledException exception);
|
||||
|
||||
Task<(Result, IQueryContext?)> tryCreateQueryContext(IRowData rowData, QueryType toApplyQueryType = QueryType.None);
|
||||
|
||||
Task<Result> tryRegisterQueryContext(IQueryContext queryContext);
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DB Row 형태의 정보를 관리하는 인터페이스 이다.
|
||||
//=============================================================================================
|
||||
public interface IRowData
|
||||
{
|
||||
string toBasicString();
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리를 요청하는 인터페이스 이다.
|
||||
//=============================================================================================
|
||||
public interface IQueryDbRequester
|
||||
{
|
||||
Task<Result> doDbRequestAsync();
|
||||
|
||||
void setQueryBatch(QueryBatchBase queryBatch);
|
||||
|
||||
Int32 getQueryCount();
|
||||
|
||||
DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifier();
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// Document 기반으로 생성해 주는 인터페이스 이다.
|
||||
//=============================================================================================
|
||||
public interface IWithDocumentModel
|
||||
{
|
||||
void onCreateDocumentModel(Table table);
|
||||
}
|
||||
|
||||
//=============================================================================================
|
||||
// DB 쿼리 정보를 담는 인터페이스 이다.
|
||||
//=============================================================================================
|
||||
public interface IQueryContext
|
||||
{
|
||||
QueryType getQueryType();
|
||||
string toBasicString();
|
||||
}
|
||||
644
ServerBase/DB/QueryRunnerWithDocument.cs
Normal file
644
ServerBase/DB/QueryRunnerWithDocument.cs
Normal file
@@ -0,0 +1,644 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using StackExchange.Redis;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//=============================================================================================
|
||||
// Amazon.DynamoDBv2.DocumentModel 기반 쿼리를 실행해 주는 클래스 이다.
|
||||
//
|
||||
// 1. 선택된 테이블에서 쿼리가 가능 하다.
|
||||
// 2. Get(Read)과 Write는 각각 트랜젝션이 보장 되므로 주의 한다
|
||||
// 3. DocumentTransactGet + DocumentTransactWrite 실행시 동일한 PK에 대한 트랜잭션 보장은 되지 않는다.
|
||||
// 4. 쿼리는 Get(Read), Write 는 최초 등록된 순서대로 실행 된다.
|
||||
//=============================================================================================
|
||||
|
||||
public partial class QueryRunnerWithDocument : IQueryRunner
|
||||
{
|
||||
private QueryBatchBase? m_query_batch;
|
||||
|
||||
private Dictionary<Table, Dictionary<Type, IQueryDbRequester>> m_reserved_db_requesters = new();
|
||||
|
||||
private List<IQueryDbRequester> m_to_try_db_requesters = new();
|
||||
|
||||
private IQueryDbRequester? m_executing_query_nullable;
|
||||
|
||||
private MultiTableDocumentBatchGet? m_batch_get;
|
||||
private MultiTableDocumentBatchWrite? m_batch_write;
|
||||
private MultiTableDocumentTransactGet? m_transact_get;
|
||||
private MultiTableDocumentTransactWrite? m_transact_write;
|
||||
|
||||
public QueryRunnerWithDocument()
|
||||
{ }
|
||||
|
||||
public async Task<Result> onTryCommitQueryRunner()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
foreach (var db_requester in m_to_try_db_requesters)
|
||||
{
|
||||
result = await db_requester.doDbRequestAsync();
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> onCommitQueryRunner()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
foreach (var db_requester in m_to_try_db_requesters)
|
||||
{
|
||||
m_executing_query_nullable = db_requester;
|
||||
|
||||
var err_msg = $"DBRequesterType:{db_requester.getTypeName()}, QueryCount:{db_requester.getQueryCount()}";
|
||||
Log.getLogger().info(err_msg);
|
||||
|
||||
if (true == db_requester.isEqualQuery<DBRequestGetWithDocumentBatchGet>())
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(m_batch_get, $"m_batch_get is null !!!");
|
||||
await m_batch_get.ExecuteAsync();
|
||||
}
|
||||
else if (true == db_requester.isEqualQuery<DBRequestWriteWithDocumentBatchWrite>())
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(m_batch_write, $"m_batch_write is null !!!");
|
||||
await m_batch_write.ExecuteAsync();
|
||||
}
|
||||
else if (true == db_requester.isEqualQuery<DBRequestGetWithDocumentTransactGet>())
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(m_transact_get, $"m_transact_get is null !!!");
|
||||
await m_transact_get.ExecuteAsync();
|
||||
}
|
||||
else if (true == db_requester.isEqualQuery<DBRequestWriteWithDocumentTransactWrite>())
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(m_transact_write, $"m_transact_write is null !!!");
|
||||
await m_transact_write.ExecuteAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
err_msg = $"Not found Query with Document !!!";
|
||||
result.setFail(ServerErrorCode.DynamoDbQueryNotFoundDocumentQuery, err_msg);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task onRollbackQueryRunner()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
m_reserved_db_requesters.Clear();
|
||||
m_to_try_db_requesters.Clear();
|
||||
|
||||
m_batch_get?.Batches.Clear();
|
||||
m_batch_write?.Batches.Clear();
|
||||
m_transact_get?.TransactionParts.Clear();
|
||||
m_transact_write?.TransactionParts.Clear();
|
||||
}
|
||||
|
||||
public async Task<Result> pushQueryContexts(List<DynamoDbDocumentQueryContext> toQueryConexts)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(toQueryConexts, $"toQueryConexts is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
foreach (var query_context in toQueryConexts)
|
||||
{
|
||||
result = await pushQueryContext(query_context);
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<Result> pushQueryContext(DynamoDbDocumentQueryContext queryContext)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!! - {queryContext.toBasicString()}");
|
||||
var dynamo_db_connector = query_batch.getDynamoDbConnector();
|
||||
var table = dynamo_db_connector.getTableByFullName(queryContext.getTableFullName());
|
||||
|
||||
if (false == queryContext.isValid())
|
||||
{
|
||||
err_msg = $"DynamoDbDocumentQueryContext invalid !!! - {queryContext.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocumentIsNullInQueryContext, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var document = queryContext.getDocument();
|
||||
NullReferenceCheckHelper.throwIfNull(document, () => $"document is null !!! - {queryContext.toBasicString()}");
|
||||
|
||||
if (false == document.isValid())
|
||||
{
|
||||
err_msg = $"DynamoDbDocument invalid !!! : {document.toBasicString()} - {queryContext.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocumentIsInvalid, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var query_type = queryContext.getQueryType();
|
||||
|
||||
switch (query_type)
|
||||
{
|
||||
case QueryType.Insert:
|
||||
{
|
||||
pushInsertQuery(table, queryContext);
|
||||
break;
|
||||
}
|
||||
case QueryType.Update:
|
||||
{
|
||||
result = await pushUpdateQuery(table, queryContext);
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QueryType.Upsert:
|
||||
{
|
||||
result = await pushUpdateQuery(table, queryContext, true);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QueryType.Delete:
|
||||
{
|
||||
pushDeleteQuery(table, queryContext);
|
||||
break;
|
||||
}
|
||||
case QueryType.ExistsDelete:
|
||||
{
|
||||
pushDeleteQueryWithExistAttribute(table, queryContext);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
err_msg = $"Invalid QueryType getQueryType() !!! : queryType:{query_type} - {queryContext.toBasicString()}, {document.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocumentQueryContextTypeInvalid, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
|
||||
var msg = $"{this.getTypeName()} : {queryContext.getQueryType().convertEnumToEnumTypeAndValueString()} : {document.toBasicString()}";
|
||||
Log.getLogger().info(msg);
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public void pushSelectQuery(Table table, DynamoDbDocumentQueryContext queryContext)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(table, $"table is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(queryContext, $"queryContext is null !!!");
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var to_select_ducument = queryContext.getDocument();
|
||||
ArgumentNullException.ThrowIfNull(to_select_ducument, $"to_select_ducument is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var get_with_transact_request = getOrAddDbRequesterWithDocument<DBRequestGetWithDocumentTransactGet>(table);
|
||||
ArgumentNullException.ThrowIfNull(get_with_transact_request, $"get_with_transact_request is null !!!");
|
||||
|
||||
var generic_type = get_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddKey(to_select_ducument);
|
||||
|
||||
db_requster = get_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var get_with_batch_request = getOrAddDbRequesterWithDocument<DBRequestGetWithDocumentBatchGet>(table);
|
||||
ArgumentNullException.ThrowIfNull(get_with_batch_request, $"get_with_batch_request is null !!!");
|
||||
|
||||
var generic_type = get_with_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddKey(to_select_ducument);
|
||||
|
||||
db_requster = get_with_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public void pushInsertQuery(Table table, DynamoDbDocumentQueryContext queryContext)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(table, $"table is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(queryContext, $"queryContext is null !!!");
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var db_connector = query_batch.getDynamoDbConnector();
|
||||
ArgumentNullException.ThrowIfNull(db_connector, $"db_connector is null !!!");
|
||||
|
||||
var to_insert_ducument = queryContext.getDocument();
|
||||
ArgumentNullException.ThrowIfNull(to_insert_ducument, $"to_insert_ducument is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var write_with_transact_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentTransactWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddDocumentToPut(to_insert_ducument, DynamoDbQueryOperationOptionConfig.getTransactWriteConfigForNotExistKey());
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var write_with_batch_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentBatchWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_batch_request, $"write_with_batch_request is null !!!");
|
||||
|
||||
var generic_type = write_with_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddDocumentToPut(to_insert_ducument);
|
||||
|
||||
db_requster = write_with_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public async Task<Result> pushUpdateQuery(Table table, DynamoDbDocumentQueryContext queryContext, bool isUpsert = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(queryContext, $"queryContext is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var dynamo_db_connector = query_batch.getDynamoDbConnector();
|
||||
|
||||
var to_update_ducument = queryContext.getDocument();
|
||||
ArgumentNullException.ThrowIfNull(to_update_ducument, $"to_update_ducument is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var write_with_transact_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentTransactWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
if (true == isUpsert)
|
||||
{
|
||||
generic_type.AddDocumentToUpdate( to_update_ducument );
|
||||
}
|
||||
else
|
||||
{
|
||||
generic_type.AddDocumentToUpdate( to_update_ducument, DynamoDbQueryOperationOptionConfig.getTransactWriteConfigForExistKey() );
|
||||
}
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (true == isUpsert)
|
||||
{
|
||||
result = await to_update_ducument.tryFillupTimestampsForUpsert(dynamo_db_connector, table.TableName);
|
||||
if(result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryFillupTimestampsForUpsert() !!! : {result.toBasicString()} - {queryContext.toBasicString()}, {to_update_ducument.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(result, var primary_key) = to_update_ducument.toPrimaryKey();
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(primary_key, () => $"primary_key is null !!!");
|
||||
|
||||
(result, _) = await dynamo_db_connector.isExistPrimaryKey(table.TableName, primary_key, true);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
var write_with_batch_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentBatchWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_batch_request, $"write_with_batch_request is null !!!");
|
||||
|
||||
var generic_type = write_with_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddDocumentToPut(to_update_ducument);
|
||||
|
||||
db_requster = write_with_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void pushDeleteQuery(Table table, DynamoDbDocumentQueryContext queryContext)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(table, $"table is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(queryContext, $"queryContext is null !!!");
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var to_delete_ducument = queryContext.getDocument();
|
||||
ArgumentNullException.ThrowIfNull(to_delete_ducument, $"to_delete_ducument is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var write_with_transact_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentTransactWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddItemToDelete(to_delete_ducument);
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var write_with_batch_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentBatchWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_batch_request, $"write_with_batch_request is null !!!");
|
||||
|
||||
var generic_type = write_with_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddItemToDelete(to_delete_ducument);
|
||||
|
||||
db_requster = write_with_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public void pushDeleteQueryWithExistAttribute(Table table, DynamoDbDocumentQueryContext queryContext)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(table, $"table is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(queryContext, $"queryContext is null !!!");
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var db_connector = query_batch.getDynamoDbConnector();
|
||||
|
||||
var to_delete_ducument = queryContext.getDocument();
|
||||
ArgumentNullException.ThrowIfNull(to_delete_ducument, $"to_delete_ducument is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var write_with_transact_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentTransactWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
generic_type.AddItemToDelete(to_delete_ducument, DynamoDbQueryOperationOptionConfig.getTransactWriteConfigForExistKey());
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var write_with_batch_request = getOrAddDbRequesterWithDocument<DBRequestWriteWithDocumentBatchWrite>(table);
|
||||
ArgumentNullException.ThrowIfNull(write_with_batch_request, $"write_with_batch_request is null !!!");
|
||||
|
||||
var generic_type = write_with_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
db_requster = write_with_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public async Task<Result> tryRegisterQueryContext(IQueryContext queryContext)
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var doc_query_context = queryContext as DynamoDbDocumentQueryContext;
|
||||
ArgumentNullReferenceCheckHelper.throwIfNull(doc_query_context, () => $"doc_query_context is null !!!");
|
||||
|
||||
result = await pushQueryContext(doc_query_context);
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<(Result, IQueryContext?)> tryCreateQueryContext(IRowData rowData, QueryType toApplyQueryType = QueryType.None)
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var doc_base = rowData as DynamoDbDocBase;
|
||||
ArgumentNullReferenceCheckHelper.throwIfNull(doc_base, () => $"doc_base is null !!! - {rowData.getTypeName()}");
|
||||
|
||||
if(QueryType.None != toApplyQueryType)
|
||||
{
|
||||
doc_base.setQueryType(toApplyQueryType);
|
||||
}
|
||||
|
||||
(result, var query_context) = await doc_base.toDocumentQueryContext<DynamoDbDocBase>();
|
||||
if(result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(query_context, () => $"query_context is null !!!");
|
||||
|
||||
return (result, query_context);
|
||||
}
|
||||
|
||||
public MultiTableDocumentBatchGet getOrNewMultiTableDocumentBatchGet()
|
||||
{
|
||||
if(null == m_batch_get)
|
||||
{
|
||||
m_batch_get = new MultiTableDocumentBatchGet();
|
||||
}
|
||||
|
||||
return m_batch_get;
|
||||
}
|
||||
|
||||
public MultiTableDocumentBatchWrite getOrNewMultiTableDocumentBatchWrite()
|
||||
{
|
||||
if (null == m_batch_write)
|
||||
{
|
||||
m_batch_write = new MultiTableDocumentBatchWrite();
|
||||
}
|
||||
|
||||
return m_batch_write;
|
||||
}
|
||||
|
||||
public MultiTableDocumentTransactGet getOrNewMultiTableDocumentTransactGet()
|
||||
{
|
||||
if (null == m_transact_get)
|
||||
{
|
||||
m_transact_get = new MultiTableDocumentTransactGet();
|
||||
}
|
||||
|
||||
return m_transact_get;
|
||||
}
|
||||
|
||||
public MultiTableDocumentTransactWrite getOrNewMultiTableDocumentTransactWrite()
|
||||
{
|
||||
if (null == m_transact_write)
|
||||
{
|
||||
m_transact_write = new MultiTableDocumentTransactWrite();
|
||||
}
|
||||
|
||||
return m_transact_write;
|
||||
}
|
||||
|
||||
public TDbRequester? getOrAddDbRequesterWithDocument<TDbRequester>(Table table)
|
||||
where TDbRequester : class, IQueryDbRequester, new()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(table, $"table is null !!!");
|
||||
|
||||
var db_requester = getDbRequester<TDbRequester>(table);
|
||||
if (null != db_requester)
|
||||
{
|
||||
return db_requester;
|
||||
}
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
db_requester = new TDbRequester();
|
||||
db_requester.setQueryBatch(query_batch);
|
||||
|
||||
var db_requester_with_document = db_requester as IWithDocumentModel;
|
||||
ArgumentNullException.ThrowIfNull(db_requester_with_document, $"db_requester_with_document is null !!!");
|
||||
|
||||
db_requester_with_document.onCreateDocumentModel(table);
|
||||
|
||||
var db_requester_type = typeof(TDbRequester);
|
||||
m_reserved_db_requesters[table][db_requester_type] = db_requester;
|
||||
|
||||
m_to_try_db_requesters.Add(db_requester);
|
||||
|
||||
return db_requester;
|
||||
}
|
||||
|
||||
public TDbRequester? getDbRequester<TDbRequester>(Table table)
|
||||
where TDbRequester : class, IQueryDbRequester
|
||||
{
|
||||
if(false == m_reserved_db_requesters.TryGetValue(table, out var found_db_requesters))
|
||||
{
|
||||
found_db_requesters = new Dictionary<Type, IQueryDbRequester>();
|
||||
m_reserved_db_requesters.Add(table, found_db_requesters);
|
||||
}
|
||||
var db_requester_type = typeof(TDbRequester);
|
||||
if(false == found_db_requesters.TryGetValue(db_requester_type, out var found_db_requester))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return found_db_requester as TDbRequester;
|
||||
}
|
||||
|
||||
private void registerExceptionHandler(IQueryDbRequester queryDbRequester, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler)
|
||||
{
|
||||
var exception_notifier = queryDbRequester.getDynamoDbQueryExceptionNotifier();
|
||||
ArgumentNullException.ThrowIfNull(exception_notifier, $"exception_notifier is null !!!");
|
||||
|
||||
exception_notifier.registerExceptionHandler(exceptionHandler);
|
||||
}
|
||||
|
||||
public async Task<(Result, Result)> onHandleTransactionCancelException(TransactionCanceledException exception)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(exception, $"exception is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
var exception_result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var exception_notifier = getDynamoDbQueryExceptionNotifierOfExecutingQuery();
|
||||
if(null == exception_notifier)
|
||||
{
|
||||
err_msg = $"Not found ExceptionNotifier !!!";
|
||||
result.setFail(ServerErrorCode.DynamoDbQueryExceptionNotifierNotFound, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return (result, exception_result);
|
||||
}
|
||||
|
||||
// 트랜잭션 취소 원인에 따라 등록된 ServerErrorCode로 재설정 한다.
|
||||
// 성공일 경우에는 ServerErrorCode를 설정 하지 않는다 !!!
|
||||
var query_count = exception.CancellationReasons.Count;
|
||||
for (var query_seq_index = 0; query_seq_index < query_count; ++query_seq_index)
|
||||
{
|
||||
var reason = exception.CancellationReasons[query_seq_index];
|
||||
// 성공일 경우 패스 !!!
|
||||
if(true == reason.isSuccess())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var query_seq_no = query_seq_index + 1;
|
||||
err_msg = $"TransactionCanceledException Reason : QuerySeq:{query_seq_no}/{query_count}, Code:{reason.Code}, Message:{reason.Message}, Item:{reason.Item}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
|
||||
var exception_handler = exception_notifier.findExceptionHandler(query_seq_no, reason.Code);
|
||||
if (null != exception_handler)
|
||||
{
|
||||
exception_result.setFail(exception_handler.ReturnToErrorCode, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return (result, exception_result);
|
||||
}
|
||||
|
||||
private DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifierOfExecutingQuery()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(m_executing_query_nullable, $"m_executing_query_nullable is null !!!");
|
||||
|
||||
return m_executing_query_nullable.getDynamoDbQueryExceptionNotifier();
|
||||
}
|
||||
}
|
||||
611
ServerBase/DB/QueryRunnerWithItemRequest.cs
Normal file
611
ServerBase/DB/QueryRunnerWithItemRequest.cs
Normal file
@@ -0,0 +1,611 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Amazon.Runtime;
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using StackExchange.Redis;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
//=============================================================================================
|
||||
// Amazon.DynamoDBv2.Model 기반 쿼리를 실행해 주는 클래스 이다.
|
||||
//
|
||||
// 1. 복수개의 테이블에 쿼리가 가능 하다.
|
||||
// 2. 세부적인 Attribute 단위 쿼리가 가능 하다.
|
||||
// 3. TransactGetItemsRequest(Read)과 TransactWriteItemsRequest(Write) 각각 트랜젝션이 보장 되므로 주의 한다.
|
||||
// 4. TransactGetItemsRequest + TransactWriteItemsRequest 실행시 동일한 PK에 대한 트랜잭션 보장은 되지 않는다.
|
||||
// 5. 쿼리는 Get(Read), Write 는 최초 등록된 순서대로 실행 된다.
|
||||
// 6. 주요 코어 처리 객체
|
||||
// BatchGetItemRequest : 배치 기반 읽기 쿼리
|
||||
// BatchWriteItemRequest : 배치 기반 쓰기 쿼리
|
||||
// UpdateItemRequest : 특정 대상을 위한 Upsert 쿼리
|
||||
// TransactGetItemsRequest : 배치 + 트랜잭션 기반 읽기 쿼리
|
||||
// TransactWriteItemsRequest : 배치 + 트랜잭션 기반 쓰기 쿼리
|
||||
//=============================================================================================
|
||||
|
||||
public partial class QueryRunnerWithItemRequest : IQueryRunner
|
||||
{
|
||||
private QueryBatchBase? m_query_batch;
|
||||
|
||||
private readonly List<IQueryDbRequester> m_db_requesters = new();
|
||||
|
||||
private IQueryDbRequester? m_executing_query_nullable;
|
||||
|
||||
private readonly List<AmazonWebServiceResponse> m_db_responses = new();
|
||||
|
||||
public QueryRunnerWithItemRequest()
|
||||
{ }
|
||||
|
||||
public async Task<Result> onTryCommitQueryRunner()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = new Result();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> onCommitQueryRunner()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
foreach (var db_requester in m_db_requesters)
|
||||
{
|
||||
m_executing_query_nullable = db_requester;
|
||||
|
||||
var err_msg = $"DBRequesterType:{db_requester.getTypeName()}, QueryCount:{db_requester.getQueryCount()}";
|
||||
Log.getLogger().info(err_msg);
|
||||
|
||||
result = await db_requester.doDbRequestAsync();
|
||||
if (result.isFail())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task onRollbackQueryRunner()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
m_db_requesters.Clear();
|
||||
|
||||
m_db_responses.Clear();
|
||||
}
|
||||
|
||||
protected async Task<Result> pushQueryContext(DynamoDbItemRequestQueryContext queryContext)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(queryContext, $"queryContext is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if (false == queryContext.isValid())
|
||||
{
|
||||
err_msg = $"DynamoDbDocumentQueryContext invalid !!! - {queryContext.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocumentIsNullInQueryContext, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var item_request = queryContext.getAmazonDynamoDBRequest();
|
||||
NullReferenceCheckHelper.throwIfNull(item_request, () => $"item_request is null !!!");
|
||||
|
||||
if (false == item_request.isValid())
|
||||
{
|
||||
err_msg = $"DynamoDbDocument invalid !!! : {item_request.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocumentIsInvalid, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var query_type = queryContext.getQueryType();
|
||||
|
||||
switch (query_type)
|
||||
{
|
||||
case QueryType.Insert:
|
||||
{
|
||||
pushInsertQuery(queryContext);
|
||||
break;
|
||||
}
|
||||
case QueryType.Update:
|
||||
{
|
||||
result = pushUpdateQuery(queryContext, false);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QueryType.Upsert:
|
||||
{
|
||||
result = pushUpdateQuery(queryContext, true);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QueryType.Delete:
|
||||
{
|
||||
pushDeleteQuery(queryContext);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
err_msg = $"Invalid QueryType getQueryType() !!! : queryType:{query_type} - {queryContext.toBasicString()}, {item_request.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbItemRequestQueryContextTypeInvalid, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
|
||||
var msg = $"{this.getTypeName()} : {queryContext.getQueryType().convertEnumToEnumTypeAndValueString()} : {item_request.toBasicString()}";
|
||||
Log.getLogger().info(msg);
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public void pushSelectQuery(DynamoDbItemRequestQueryContext queryContext)
|
||||
{
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var to_select_db_item_request = queryContext.getAmazonDynamoDBRequest() as GetItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(to_select_db_item_request, $"to_select_db_item_request is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var transactGetItem = new TransactGetItem()
|
||||
{
|
||||
Get = new Get()
|
||||
{
|
||||
TableName = to_select_db_item_request.TableName,
|
||||
Key = to_select_db_item_request.Key
|
||||
}
|
||||
};
|
||||
|
||||
var get_with_transact_request = getOrAddDbRequester<DBRequestGetWithTransact>();
|
||||
ArgumentNullException.ThrowIfNull(get_with_transact_request, $"get_with_transact_request is null !!!");
|
||||
var generic_type = get_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.TransactItems.Add(transactGetItem);
|
||||
|
||||
db_requster = get_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var get_batch_request = getOrAddDbRequester<DBRequestGetBatch>();
|
||||
ArgumentNullException.ThrowIfNull(get_batch_request, $"get_batch_request is null !!!");
|
||||
var generic_type = get_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.RequestItems.TryGetValue(to_select_db_item_request.TableName, out var get_requests);
|
||||
if (null == get_requests)
|
||||
{
|
||||
get_requests = new KeysAndAttributes();
|
||||
generic_type.RequestItems.Add(to_select_db_item_request.TableName, get_requests);
|
||||
}
|
||||
|
||||
get_requests.Keys.Add(to_select_db_item_request.Key);
|
||||
|
||||
db_requster = get_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public void pushInsertQuery(DynamoDbItemRequestQueryContext queryContext)
|
||||
{
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var to_insert_db_item_request = queryContext.getAmazonDynamoDBRequest() as PutItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(to_insert_db_item_request, $"to_insert_db_item_request is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
to_insert_db_item_request.fillupTimestamps();
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var transactWriteItem = new TransactWriteItem()
|
||||
{
|
||||
Put = new Put()
|
||||
{
|
||||
TableName = to_insert_db_item_request.TableName,
|
||||
Item = to_insert_db_item_request.Item,
|
||||
ReturnValuesOnConditionCheckFailure = Amazon.DynamoDBv2.ReturnValuesOnConditionCheckFailure.ALL_OLD,
|
||||
ConditionExpression = to_insert_db_item_request.ConditionExpression,
|
||||
}
|
||||
};
|
||||
|
||||
var write_with_transact_request = getOrAddDbRequester<DBRequestWriteWithTransact>();
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.TransactItems.Add(transactWriteItem);
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var write_batch_request = getOrAddDbRequester<DBRequestWriteBatch>();
|
||||
ArgumentNullException.ThrowIfNull(write_batch_request, $"write_batch_request is null !!!");
|
||||
var generic_type = write_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.RequestItems.TryGetValue(to_insert_db_item_request.TableName, out var write_requests);
|
||||
if (null == write_requests)
|
||||
{
|
||||
write_requests = new List<WriteRequest>();
|
||||
generic_type.RequestItems.Add(to_insert_db_item_request.TableName, write_requests);
|
||||
}
|
||||
|
||||
write_requests.Add( new WriteRequest {
|
||||
PutRequest = new PutRequest {
|
||||
Item = to_insert_db_item_request.Item
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
db_requster = write_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public Result pushUpdateQuery(DynamoDbItemRequestQueryContext queryContext, bool isUpsert = false)
|
||||
{
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var to_update_db_item_request = queryContext.getAmazonDynamoDBRequest() as UpdateItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(to_update_db_item_request, $"to_update_db_item_request is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if(to_update_db_item_request.UpdateExpression.isNullOrWhiteSpace())
|
||||
{
|
||||
err_msg = $"UpdateItemRequest.UpdateExpression is Empty !!! - {queryContext.toBasicString()}, {to_update_db_item_request.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbItemRequestUpdateExpressionEmpty, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result = to_update_db_item_request.tryFillupTimestamps(isUpsert);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryFillupTimestamps() !!! : {result.toBasicString()} - {queryContext.toBasicString()}, {to_update_db_item_request.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var transactWriteItem = new TransactWriteItem()
|
||||
{
|
||||
Update = new Update()
|
||||
{
|
||||
TableName = to_update_db_item_request.TableName,
|
||||
Key = to_update_db_item_request.Key,
|
||||
ExpressionAttributeNames = to_update_db_item_request.ExpressionAttributeNames,
|
||||
ExpressionAttributeValues = to_update_db_item_request.ExpressionAttributeValues,
|
||||
ConditionExpression = to_update_db_item_request.ConditionExpression,
|
||||
UpdateExpression = to_update_db_item_request.UpdateExpression,
|
||||
ReturnValuesOnConditionCheckFailure = Amazon.DynamoDBv2.ReturnValuesOnConditionCheckFailure.ALL_OLD,
|
||||
}
|
||||
};
|
||||
|
||||
var write_with_transact_request = getOrAddDbRequester<DBRequestWriteWithTransact>();
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.TransactItems.Add(transactWriteItem);
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var update_request = getAfterAddDbRequester<DBRequestUpdate>();
|
||||
ArgumentNullException.ThrowIfNull(update_request, $"update_request is null !!!");
|
||||
|
||||
var generic_type = update_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.TableName = to_update_db_item_request.TableName;
|
||||
generic_type.Key = to_update_db_item_request.Key;
|
||||
generic_type.ExpressionAttributeNames = to_update_db_item_request.ExpressionAttributeNames;
|
||||
generic_type.ExpressionAttributeValues = to_update_db_item_request.ExpressionAttributeValues;
|
||||
generic_type.ConditionExpression = to_update_db_item_request.ConditionExpression;
|
||||
generic_type.UpdateExpression = to_update_db_item_request.UpdateExpression;
|
||||
generic_type.ReturnValues = to_update_db_item_request.ReturnValues;
|
||||
|
||||
db_requster = update_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void pushDeleteQuery(DynamoDbItemRequestQueryContext queryContext)
|
||||
{
|
||||
var query_batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(query_batch, $"query_batch is null !!!");
|
||||
|
||||
var to_delete_db_item_request = queryContext.getAmazonDynamoDBRequest() as DeleteItemRequest;
|
||||
ArgumentNullException.ThrowIfNull(to_delete_db_item_request, $"to_delete_db_item_request is null !!!");
|
||||
|
||||
IQueryDbRequester db_requster;
|
||||
|
||||
if (true == query_batch.isUseTransact())
|
||||
{
|
||||
var transactWriteItem = new TransactWriteItem()
|
||||
{
|
||||
Delete = new Delete()
|
||||
{
|
||||
TableName = to_delete_db_item_request.TableName,
|
||||
Key = to_delete_db_item_request.Key,
|
||||
ReturnValuesOnConditionCheckFailure = Amazon.DynamoDBv2.ReturnValuesOnConditionCheckFailure.ALL_OLD,
|
||||
}
|
||||
};
|
||||
|
||||
var write_with_transact_request = getOrAddDbRequester<DBRequestWriteWithTransact>();
|
||||
ArgumentNullException.ThrowIfNull(write_with_transact_request, $"write_with_transact_request is null !!!");
|
||||
var generic_type = write_with_transact_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.TransactItems.Add(transactWriteItem);
|
||||
|
||||
db_requster = write_with_transact_request;
|
||||
}
|
||||
else
|
||||
{
|
||||
var write_batch_request = getOrAddDbRequester<DBRequestWriteBatch>();
|
||||
ArgumentNullException.ThrowIfNull(write_batch_request, $"write_batch_request is null !!!");
|
||||
var generic_type = write_batch_request.getDbRequestGenericType();
|
||||
ArgumentNullException.ThrowIfNull(generic_type, $"generic_type is null !!!");
|
||||
|
||||
generic_type.RequestItems.TryGetValue(to_delete_db_item_request.TableName, out var write_requests);
|
||||
if (null == write_requests)
|
||||
{
|
||||
write_requests = new List<WriteRequest>();
|
||||
generic_type.RequestItems.Add(to_delete_db_item_request.TableName, write_requests);
|
||||
}
|
||||
|
||||
write_requests.Add( new WriteRequest {
|
||||
DeleteRequest = new DeleteRequest {
|
||||
Key = to_delete_db_item_request.Key
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
db_requster = write_batch_request;
|
||||
}
|
||||
|
||||
registerExceptionHandler(db_requster, queryContext.getExceptionHandler());
|
||||
}
|
||||
|
||||
public async Task<Result> tryRegisterQueryContext(IQueryContext queryContext)
|
||||
{
|
||||
var query_batch = getQueryBatch();
|
||||
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
|
||||
DynamoDbItemRequestQueryContext? item_request_query_context;
|
||||
|
||||
if(queryContext is DynamoDbDocumentQueryContext document_query_context)
|
||||
{
|
||||
(result, item_request_query_context) = document_query_context.getDocument().toItemRequestQueryContext(document_query_context.getTableFullName(), queryContext.getQueryType());
|
||||
if(result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item_request_query_context = queryContext as DynamoDbItemRequestQueryContext;
|
||||
}
|
||||
ArgumentNullReferenceCheckHelper.throwIfNull(item_request_query_context, () => $"item_request_query_context is null !!!");
|
||||
|
||||
result = await pushQueryContext(item_request_query_context);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<(Result, IQueryContext?)> tryCreateQueryContext(IRowData rowData, QueryType toApplyQueryType = QueryType.None)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!!");
|
||||
var dynamo_db_connector = query_batch.getDynamoDbConnector();
|
||||
|
||||
var doc_base = rowData as DynamoDbDocBase;
|
||||
ArgumentNullReferenceCheckHelper.throwIfNull(doc_base, () => $"doc_base is null !!! - {rowData.getTypeName()}");
|
||||
|
||||
if (QueryType.None != toApplyQueryType)
|
||||
{
|
||||
doc_base.setQueryType(toApplyQueryType);
|
||||
}
|
||||
|
||||
var query_type = doc_base.getQueryType();
|
||||
if (QueryType.None == query_type)
|
||||
{
|
||||
err_msg = $"Invalid QueryType !!! : QueryType.None != DocBase.QueryType:{query_type} - {doc_base.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DbQueryTypeInvalid, err_msg);
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
(result, var document) = await doc_base.onCopyToDocument();
|
||||
if(result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(document, () => $"document is null !!! - {doc_base.toBasicString()}");
|
||||
|
||||
(result, var item_request_query_context) = document.toItemRequestQueryContext(dynamo_db_connector.getTableFullName(doc_base.TableName), doc_base.getQueryType());
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, item_request_query_context);
|
||||
}
|
||||
|
||||
|
||||
public TDbRequester? getOrAddDbRequester<TDbRequester>()
|
||||
where TDbRequester : class, IQueryDbRequester, new()
|
||||
{
|
||||
var db_requester = getDbRequester<TDbRequester>();
|
||||
if (null != db_requester)
|
||||
{
|
||||
return db_requester;
|
||||
}
|
||||
|
||||
db_requester = new TDbRequester();
|
||||
|
||||
var batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(batch, $"batch is null !!!");
|
||||
|
||||
db_requester.setQueryBatch(batch);
|
||||
|
||||
m_db_requesters.Add(db_requester);
|
||||
|
||||
return getDbRequester<TDbRequester>();
|
||||
}
|
||||
|
||||
public TDbRequester? getAfterAddDbRequester<TDbRequester>()
|
||||
where TDbRequester : class, IQueryDbRequester, new()
|
||||
{
|
||||
var db_requester = new TDbRequester();
|
||||
|
||||
var batch = getQueryBatch();
|
||||
ArgumentNullException.ThrowIfNull(batch, $"batch is null !!!");
|
||||
|
||||
db_requester.setQueryBatch(batch);
|
||||
|
||||
m_db_requesters.Add(db_requester);
|
||||
|
||||
return db_requester;
|
||||
}
|
||||
|
||||
public TDbResponseType? getDbResponse<TDbResponseType>()
|
||||
where TDbResponseType : AmazonWebServiceResponse
|
||||
{
|
||||
foreach (var db_response in m_db_responses)
|
||||
{
|
||||
var response_type = db_response as TDbResponseType;
|
||||
if (null != response_type)
|
||||
{
|
||||
return response_type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public TDbRequester? getDbRequester<TDbRequester>()
|
||||
where TDbRequester : class, IQueryDbRequester
|
||||
{
|
||||
foreach (var db_requester in m_db_requesters)
|
||||
{
|
||||
var to_return_type = db_requester as TDbRequester;
|
||||
if (null != to_return_type)
|
||||
{
|
||||
return to_return_type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerExceptionHandler(IQueryDbRequester queryDbRequester, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler)
|
||||
{
|
||||
var exception_notifier = queryDbRequester.getDynamoDbQueryExceptionNotifier();
|
||||
ArgumentNullException.ThrowIfNull(exception_notifier, $"exception_notifier is null !!!");
|
||||
|
||||
exception_notifier.registerExceptionHandler(exceptionHandler);
|
||||
}
|
||||
|
||||
public async Task<(Result, Result)> onHandleTransactionCancelException(TransactionCanceledException exception)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(exception, $"exception is null !!!");
|
||||
|
||||
var result = new Result();
|
||||
var exception_resullt = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var exception_notifier = getDynamoDbQueryExceptionNotifierOfExecutingQuery();
|
||||
if (null == exception_notifier)
|
||||
{
|
||||
err_msg = $"Not found ExceptionNotifier !!!";
|
||||
result.setFail(ServerErrorCode.DynamoDbQueryExceptionNotifierNotFound, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return (result, exception_resullt);
|
||||
}
|
||||
|
||||
// 트랜잭션 취소 원인에 따라 등록된 ServerErrorCode로 재설정 한다.
|
||||
var query_count = exception.CancellationReasons.Count;
|
||||
for (var query_seq_index = 0; query_seq_index < query_count; ++query_seq_index)
|
||||
{
|
||||
var reason = exception.CancellationReasons[query_seq_index];
|
||||
// 성공일 경우 패스 !!!
|
||||
if (true == reason.isSuccess())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var query_seq_no = query_seq_index + 1;
|
||||
err_msg = $"TransactionCanceledException Reason : QuerySeq:{query_seq_no}/{query_count}, Code:{reason.Code}, Message:{reason.Message}, Item:{reason.Item}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
|
||||
var exception_handler = exception_notifier.findExceptionHandler(query_seq_no, reason.Code);
|
||||
if (null != exception_handler)
|
||||
{
|
||||
exception_resullt.setFail(exception_handler.ReturnToErrorCode, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return (result, exception_resullt);
|
||||
}
|
||||
|
||||
private DynamoDbQueryExceptionNotifier getDynamoDbQueryExceptionNotifierOfExecutingQuery()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(m_executing_query_nullable, $"m_executing_query_nullable is null !!!");
|
||||
|
||||
return m_executing_query_nullable.getDynamoDbQueryExceptionNotifier();
|
||||
}
|
||||
}
|
||||
194
ServerBase/DB/Query_GetSet.cs
Normal file
194
ServerBase/DB/Query_GetSet.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
|
||||
using Amazon.Runtime;
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
|
||||
namespace ServerBase;
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// QueryContextBase 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryExecutorBase
|
||||
{
|
||||
public void setQueryBatch(QueryBatchBase parent) => m_parent = parent;
|
||||
|
||||
public QueryBatchBase? getQueryBatch() => m_parent;
|
||||
|
||||
public string getQueryName() => m_query_name;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// QueryBatchBase 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryBatchBase
|
||||
{
|
||||
public bool isUseTransact() => m_is_use_transact;
|
||||
|
||||
public string getTransId() => m_trans_id;
|
||||
|
||||
public bool hasQuery() => 0 < m_querys.Count;
|
||||
|
||||
public Int32 getQueryCount() => m_querys.Count;
|
||||
|
||||
public IWithLogActor getLogActor() => m_log_actor;
|
||||
|
||||
public void setLogActor(IWithLogActor logActor) => m_log_actor = logActor;
|
||||
|
||||
public IQueryRunner getQueryRunner() => m_query_runner;
|
||||
|
||||
public DynamoDbClient getDynamoDbConnector() => m_dynamo_db_connector;
|
||||
|
||||
public LogAction getLogAction() => m_log_action;
|
||||
|
||||
public List<ILogInvoker> getLogInvokers() => m_business_logs;
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// QueryRunnerWithItemRequest 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public partial class QueryRunnerWithItemRequest : IQueryRunner
|
||||
{
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
|
||||
public List<IQueryDbRequester> getDbRequesters() => m_db_requesters;
|
||||
|
||||
public List<AmazonWebServiceResponse> getDbResponses() => m_db_responses;
|
||||
|
||||
public void addDbResponse(AmazonWebServiceResponse dbResponse)
|
||||
{
|
||||
m_db_responses.Add(dbResponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// QueryRunnerWithDocument 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public partial class QueryRunnerWithDocument : IQueryRunner
|
||||
{
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// QueryDbRequesterWithItemRequestBase<TDbRequestType> 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithItemRequestBase<TDbRequestType> : IQueryDbRequester
|
||||
{
|
||||
public TDbRequestType getDbRequestGenericType() => m_db_request;
|
||||
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
|
||||
public Int32 getQueryCount()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(m_query_batch, () => $"m_query_batch is null !!!");
|
||||
return m_query_batch.getQueryCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// QueryDbRequesterWithDocumentBatchWriteBase<TDbRequestType> 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentBatchWriteBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentBatchWrite
|
||||
{
|
||||
public TDbRequestType? getDbRequestGenericType() => m_db_request;
|
||||
|
||||
public Table? getTable() => m_table;
|
||||
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
|
||||
public Int32 getQueryCount()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(m_query_batch, () => $"m_query_batch is null !!!");
|
||||
return m_query_batch.getQueryCount();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// QueryDbRequesterWithDocumentBatchGetBase<TDbRequestType> 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentBatchGetBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentBatchGet
|
||||
{
|
||||
public TDbRequestType? getDbRequestGenericType() => m_db_request;
|
||||
|
||||
public Table? getTable() => m_table;
|
||||
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
|
||||
public Int32 getQueryCount()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(m_query_batch, () => $"m_query_batch is null !!!");
|
||||
return m_query_batch.getQueryCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// QueryDbRequesterWithDocumentTransactWriteBase<TDbRequestType> 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentTransactWriteBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentTransactWrite
|
||||
{
|
||||
public TDbRequestType? getDbRequestGenericType() => m_db_request;
|
||||
|
||||
public Table? getTable() => m_table;
|
||||
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
|
||||
public Int32 getQueryCount()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(m_query_batch, () => $"m_query_batch is null !!!");
|
||||
return m_query_batch.getQueryCount();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// QueryDbRequesterWithDocumentTransactGetBase<TDbRequestType> 의 GetSet 함수들
|
||||
//==============================================================================================
|
||||
public abstract partial class QueryDbRequesterWithDocumentTransactGetBase<TDbRequestType> : IQueryDbRequester, IWithDocumentModel
|
||||
where TDbRequestType : DocumentTransactGet
|
||||
{
|
||||
public TDbRequestType? getDbRequestGenericType() => m_db_request;
|
||||
|
||||
public Table? getTable() => m_table;
|
||||
|
||||
public void setQueryBatch(QueryBatchBase queryBatch) => m_query_batch = queryBatch;
|
||||
|
||||
public QueryBatchBase? getQueryBatch() => m_query_batch;
|
||||
|
||||
public Int32 getQueryCount()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(m_query_batch, () => $"m_query_batch is null !!!");
|
||||
return m_query_batch.getQueryCount();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user