398 lines
12 KiB
C#
398 lines
12 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
using NeoSmart;
|
|
using NeoSmart.AsyncLock;
|
|
|
|
|
|
using ServerCore; using ServerBase;
|
|
|
|
|
|
using SESSION_ID = System.Int32;
|
|
using WORLD_ID = System.UInt32;
|
|
using META_ID = System.UInt32;
|
|
using ENTITY_GUID = System.String;
|
|
using ACCOUNT_ID = System.String;
|
|
using OWNER_GUID = System.String;
|
|
using USER_GUID = System.String;
|
|
using CHARACTER_GUID = System.String;
|
|
using ITEM_GUID = System.String;
|
|
|
|
|
|
namespace ServerBase;
|
|
|
|
|
|
public abstract partial class EntityAttributeBase : IInitializer
|
|
{
|
|
public enum StateType
|
|
{
|
|
None = 0,
|
|
|
|
Created, // 신규 정보로 추가 한다. delta count == Stack count !!!
|
|
Modified, // +, - : 증가, 감소
|
|
Removed, // 제거
|
|
}
|
|
|
|
public class AttributeState : Flags<StateType>
|
|
{
|
|
public AttributeState()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
public void tryApplyModifyFlag(bool isTrue)
|
|
{
|
|
setFlag(StateType.Modified, isTrue);
|
|
}
|
|
}
|
|
|
|
#region 소유자라고 설정한 객체
|
|
private EntityBase m_owner;
|
|
#endregion
|
|
|
|
private bool m_is_origin = true;
|
|
|
|
private DynamoDbDocBase? m_origin_doc_base_nullable;
|
|
|
|
private DynamoDbDocBase? m_try_pending_doc_base_nullable;
|
|
|
|
// CommonResult에 적용 되는가?
|
|
private readonly bool m_is_applicable_to_common_result;
|
|
|
|
private AttributeState m_attribute_state = new();
|
|
|
|
private bool m_is_upsert = false;
|
|
|
|
private bool m_is_delete = false;
|
|
|
|
private EntityRecorder? m_recorder_nullable;
|
|
|
|
#region Owner Contents 관계에 맞게 저장되는 소유자 정보[OwnerEntityType, EntityBase]
|
|
private OwnerEntityType m_owner_entity_type = OwnerEntityType.None;
|
|
private EntityBase? m_entity_of_owner_entity_type = null;
|
|
#endregion
|
|
|
|
private ReaderWriterLockSlim m_rw_lock = new();
|
|
|
|
private AsyncLock m_async_lock = new();
|
|
|
|
//=========================================================================================
|
|
// Owner 객체와 OwnerEntityType 객체가 동일한 경우
|
|
//=========================================================================================
|
|
public EntityAttributeBase(EntityBase owner, bool isApplicableCommonResult = false)
|
|
{
|
|
m_owner = owner;
|
|
|
|
setEntityOfOwnerEntityType(owner.onGetOwnerEntityType(), owner);
|
|
|
|
m_is_applicable_to_common_result = isApplicableCommonResult;
|
|
}
|
|
|
|
//=========================================================================================
|
|
// Owner 객체와 OwnerEntityType 객체가 다른 경우
|
|
//=========================================================================================
|
|
public EntityAttributeBase(EntityBase owner, EntityBase entityOfOwnerEntityType, bool isApplicableCommonResult = false)
|
|
{
|
|
if(false == entityOfOwnerEntityType.isValidOwnerEntityType())
|
|
{
|
|
throw new ArgumentException($"argument exception isValidOwnerEntityType() !!! : {entityOfOwnerEntityType.toBasicString()} - {toBasicString()}");
|
|
}
|
|
|
|
m_owner = owner;
|
|
|
|
setEntityOfOwnerEntityType(entityOfOwnerEntityType.onGetOwnerEntityType(), entityOfOwnerEntityType);
|
|
|
|
m_is_applicable_to_common_result = isApplicableCommonResult;
|
|
}
|
|
|
|
public virtual async Task<Result> onInit()
|
|
{
|
|
var result = new Result();
|
|
|
|
return await Task.FromResult(result);
|
|
}
|
|
|
|
public abstract void onClear();
|
|
|
|
public virtual void newEntityAttribute()
|
|
{
|
|
getAttributeState().setFlag(StateType.Created, true);
|
|
}
|
|
|
|
public virtual void deleteEntityAttribute()
|
|
{
|
|
getAttributeState().reset();
|
|
m_is_upsert = false;
|
|
|
|
getAttributeState().setFlag(StateType.Removed, true);
|
|
}
|
|
|
|
public virtual void modifiedEntityAttribute(bool isUpsert = false)
|
|
{
|
|
if (true == getAttributeState().hasFlag(StateType.Created))
|
|
{
|
|
return;
|
|
}
|
|
|
|
getAttributeState().setFlag(StateType.Modified, true);
|
|
if(true == isUpsert)
|
|
{
|
|
m_is_upsert = isUpsert;
|
|
}
|
|
}
|
|
|
|
public abstract IEntityAttributeTransactor onNewEntityAttributeTransactor();
|
|
|
|
public abstract EntityAttributeBase onCloned();
|
|
|
|
|
|
|
|
protected void deepCopyFromBase(EntityAttributeBase other)
|
|
{
|
|
m_attribute_state.deepCopy(other.getAttributeState());
|
|
}
|
|
|
|
|
|
|
|
public virtual DynamoDbDocBase? onCreateDocBase()
|
|
{
|
|
var err_msg = $"Not implementation EntityAttribute.onCreateDocBase() !!! : {this.getTypeName()} - {getOwner().toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
throw new NotImplementedException(err_msg);
|
|
}
|
|
|
|
public virtual async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true)
|
|
{
|
|
var result = new Result();
|
|
|
|
var err_msg = $"Not implementation EntityAttribute.toDocBase() !!! : {this.getTypeName()} - {getOwner().toBasicString()}";
|
|
result.setFail(ServerErrorCode.NotImplemented, err_msg);
|
|
Log.getLogger().warn(result.toBasicString());
|
|
|
|
return await Task.FromResult<(Result, DynamoDbDocBase?)>((result, null));
|
|
}
|
|
|
|
public virtual async Task<Result> fillupDocBase<TDoc>(TDoc copyToDocBase)
|
|
where TDoc : DynamoDbDocBase
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var owner = getOwner();
|
|
var parent = owner.getRootParent();
|
|
|
|
var copy_to_doc = copyToDocBase as ICopyDocFromEntityAttribute;
|
|
if(null == copy_to_doc)
|
|
{
|
|
err_msg = $"Failed to cast ICopyDocFromEntityAttribute !!! : {copyToDocBase.getTypeName()} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.ClassDoesNotImplementInterfaceInheritance, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if(false == copy_to_doc.copyDocFromEntityAttribute(this))
|
|
{
|
|
err_msg = $"Failed to copyDocFromEntityAttribute() !!!, to:{copyToDocBase.getTypeName()}, from:{this.getTypeName()} - {toSummaryString()}, {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
return await Task.FromResult(result);
|
|
}
|
|
|
|
public async Task<Result> fillupDoc4QueryWithAttribute<TDoc>(TDoc docBase)
|
|
where TDoc : DynamoDbDocBase
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var owner = getOwner();
|
|
var parent = owner.getRootParent();
|
|
|
|
result = await fillupDocBase<TDoc>(docBase);
|
|
if (result.isFail())
|
|
{
|
|
err_msg = $"Failed to fillupDocBase() !!! : {result.toBasicString()} - {owner.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
return result;
|
|
}
|
|
|
|
(result, _) = await applyDoc4Query(docBase);
|
|
if(result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected async Task<(Result, DynamoDbDocBase?)> applyDoc4Query(DynamoDbDocBase docBase)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var owner = getOwner();
|
|
var parent = owner.getRootParent();
|
|
|
|
bool is_apply = false;
|
|
|
|
if (true == getAttributeState().hasFlag(StateType.Created))
|
|
{
|
|
result = await docBase.newDoc4Query();
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
is_apply = true;
|
|
}
|
|
else
|
|
{
|
|
if (true == getAttributeState().hasFlag(StateType.Modified))
|
|
{
|
|
if (true == m_is_upsert)
|
|
{
|
|
result = await docBase.upsertDoc4Query();
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = await docBase.updateDoc4Query();
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
}
|
|
|
|
m_is_upsert = false;
|
|
is_apply = true;
|
|
}
|
|
|
|
if (true == getAttributeState().hasFlag(StateType.Removed))
|
|
{
|
|
result = await docBase.deleteDoc4Query();
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
is_apply = true;
|
|
|
|
setDelete();
|
|
}
|
|
}
|
|
|
|
if (false == is_apply)
|
|
{
|
|
err_msg = $"Invalid AttributeState !!!, all false state - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.DynamoDbDocAttributeStateNotSet, err_msg);
|
|
|
|
return (result, null);
|
|
}
|
|
|
|
getAttributeState().reset();
|
|
|
|
return (result, docBase);
|
|
}
|
|
|
|
public void enterReadLock()
|
|
{
|
|
m_rw_lock.EnterReadLock();
|
|
}
|
|
|
|
public void exitReadLock()
|
|
{
|
|
m_rw_lock.ExitReadLock();
|
|
}
|
|
|
|
public void enterWriteLock()
|
|
{
|
|
m_rw_lock.EnterWriteLock();
|
|
}
|
|
|
|
public void exitWriteLock()
|
|
{
|
|
m_rw_lock.ExitWriteLock();
|
|
}
|
|
|
|
public bool hasQueryFlag()
|
|
{
|
|
return 0 < getAttributeState().getData().Count;
|
|
}
|
|
|
|
#region DB 저장과 관련된 함수들
|
|
|
|
//=================================================================================================
|
|
// EntityAttribute로 Db에 저장할 OwnerEntity를 반환해 준다.
|
|
//=================================================================================================
|
|
public virtual (Result, OwnerEntityType, OWNER_GUID) onToOwnerEntity4Db(EntityBase ownerEntity)
|
|
{
|
|
var result = new Result();
|
|
result.setFail(ServerErrorCode.NotImplemented, $"Not implemented onToOwnerEntity4Db() !!! - {getOwner().toBasicString()}");
|
|
|
|
return (result, OwnerEntityType.None, string.Empty);
|
|
}
|
|
|
|
//=========================================================================================
|
|
// DB 에 저장되어 있는 정보가 있는 경우 호출되어야 한다.
|
|
//=========================================================================================
|
|
public void syncOriginDocBaseWithNewDoc<EntityAttributeType>(DynamoDbDocBase newDoc)
|
|
where EntityAttributeType : EntityAttributeBase
|
|
{
|
|
if(true == isOrigin())
|
|
{
|
|
m_origin_doc_base_nullable = newDoc;
|
|
return;
|
|
}
|
|
|
|
var owner = getOwner();
|
|
var origin_entity_attribute = owner.getOriginEntityAttribute<EntityAttributeType>();
|
|
NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
origin_entity_attribute.syncOriginDocBaseWithNewDoc<EntityAttributeType>(newDoc);
|
|
}
|
|
|
|
public DynamoDbDocBase? getOriginDocBase<EntityAttributeType>()
|
|
where EntityAttributeType : EntityAttributeBase
|
|
{
|
|
if (true == isOrigin())
|
|
{
|
|
return m_origin_doc_base_nullable;
|
|
}
|
|
|
|
var owner = getOwner();
|
|
var origin_entity_attribute = owner.getOriginEntityAttribute<EntityAttributeType>();
|
|
NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
return origin_entity_attribute.getOriginDocBase<EntityAttributeType>();
|
|
}
|
|
|
|
#endregion DB 저장과 관련된 함수들
|
|
|
|
|
|
#region 로그와 관련된 함수들
|
|
|
|
public virtual string toSummaryString()
|
|
{
|
|
return $"{this.getTypeName()}, {JsonConvert.SerializeObject(this)}";
|
|
}
|
|
|
|
public virtual string toBasicString()
|
|
{
|
|
return $"{this.getTypeName()}, isOrigin:{isOrigin()}, hasQueryFlag:{hasQueryFlag()}";
|
|
}
|
|
|
|
#endregion 로그와 관련된 함수들
|
|
}
|