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 { 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 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 fillupDocBase(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 fillupDoc4QueryWithAttribute(TDoc docBase) where TDoc : DynamoDbDocBase { var result = new Result(); var err_msg = string.Empty; var owner = getOwner(); var parent = owner.getRootParent(); result = await fillupDocBase(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(DynamoDbDocBase newDoc) where EntityAttributeType : EntityAttributeBase { if(true == isOrigin()) { m_origin_doc_base_nullable = newDoc; return; } var owner = getOwner(); var origin_entity_attribute = owner.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); origin_entity_attribute.syncOriginDocBaseWithNewDoc(newDoc); } public DynamoDbDocBase? getOriginDocBase() where EntityAttributeType : EntityAttributeBase { if (true == isOrigin()) { return m_origin_doc_base_nullable; } var owner = getOwner(); var origin_entity_attribute = owner.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); return origin_entity_attribute.getOriginDocBase(); } #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 로그와 관련된 함수들 }