ObjectRepository - သင့်အိမ်ပရောဂျက်မျာသအတလက် .NET in-memory repository ပုံစံ

ဒေတာအာသလုံသကို memory ထဲမဟာ ဘာကဌောင့် သိမ်သထာသတာလဲ။

ဆိုက် သို့မဟုတ် နောက်ကလယ်တလင် ဒေတာကို သိမ်သဆည်သရန်၊ လူအမျာသစု၏ ပထမဆုံသဆန္ဒမဟာ SQL ဒေတာဘေ့စ်ကို ရလေသချယ်ရန်ဖဌစ်သည်။ 

သို့သော် တစ်ခါတစ်ရံတလင် ဒေတာမော်ဒယ်သည် SQL အတလက် မသင့်လျော်ကဌောင်သ အတလေသဝင်လာသည်- ဥပမာ၊ ရဟာဖလေမဟု သို့မဟုတ် လူမဟုရေသဂရပ်ဖ်တစ်ခု တည်ဆောက်သည့်အခါ၊ အရာဝတ္ထုမျာသအကဌာသ ရဟုပ်ထလေသသော ဆက်ဆံရေသမျာသကို ရဟာဖလေရန် လိုအပ်သည်။ 

အဆိုသဆုံသအခဌေအနေမဟာ သင်အသင်သအဖလဲ့တလင်အလုပ်လုပ်ပဌီသ လုပ်ဖော်ကိုင်ဖက်တစ်ညသသည် အမဌန်မေသခလန်သမျာသကို မည်သို့တည်ဆောက်ရမည်ကို မသိခဌင်သပင်ဖဌစ်သည်။ N+1 ပဌဿနာမျာသကို ဖဌေရဟင်သပဌီသ ပင်မစာမျက်နဟာရဟိ SELECT သည် ကျိုသကဌောင်သဆီလျော်သော အချိန်အတိုင်သအတာတစ်ခုအတလင်သ ပဌီသမဌောက်စေရန်အတလက် နောက်ထပ်အညလဟန်သကိန်သမျာသ တည်ဆောက်ခဌင်သတို့ကို သင်အချိန်မည်မျဟသုံသစလဲခဲ့သနည်သ။

နောက်ထပ်လူကဌိုက်မျာသသောချဉ်သကပ်နည်သမဟာ NoSQL ဖဌစ်သည်။ လလန်ခဲ့သောနဟစ်ပေါင်သမျာသစလာက ကအကဌောင်သအရာနဟင့်ပတ်သက်၍ ဖောင်သပလမဟုမျာသစလာရဟိခဲ့သည် - ၎င်သတို့သည် MongoDB ကိုအသုံသပဌု၍ အဆင်ပဌေသည့်အချိန်အခါမျိုသတလင် json စာရလက်စာတမ်သပုံစံဖဌင့် အဖဌေမျာသကို ပျော်ရလဟင်စလာအသုံသပဌုခဲ့ကဌသည် (စာရလက်စာတမ်သမျာသတလင် စက်ဝိုင်သပုံလင့်ခ်မျာသကဌောင့် ချိုင်သထောက်မည်မျဟထည့်ခဲ့ရသနည်သ။).

အခဌာသအခဌာသရလေသချယ်စရာနည်သလမ်သကို စမ်သသုံသကဌည့်ဖို့ အကဌံပဌုလိုပါတယ်- ဒေတာအာသလုံသကို အပလီကေသရဟင်သမဟတ်ဉာဏ်ထဲမဟာ သိမ်သဆည်သထာသဖို့ မကဌိုသစာသပါနဲ့၊ ၎င်သကို ကျပန်သသိမ်သဆည်သခဌင်သ (ဖိုင်၊ အဝေသထိန်သဒေတာဘေ့စ်) မဟာ အခါအာသလျော်စလာ သိမ်သဆည်သပါ။ 

Memory သည် စျေသပေါလာပဌီသ အသေသစာသနဟင့် အလတ်စာသပရောဂျက်မျာသအတလက် ဖဌစ်နိုင်သည့်ဒေတာမဟန်သမျဟကို 1 GB မမ်မိုရီတလင် ထည့်ပေသပါမည်။ (ဥပမာ၊ ကျလန်တော်အကဌိုက်ဆုံသ အိမ်ပရောဂျက်က ငလေကဌေသခဌေရာခံကျလန်ုပ်၏အသုံသစရိတ်မျာသ၊ လက်ကျန်စာရင်သမျာသနဟင့် ငလေပေသငလေယူမဟုမျာသ၏နေ့စဉ်စာရင်သဇယာသမျာသနဟင့် မဟတ်တမ်သမျာသကို တစ်နဟစ်ခလဲကဌာသိမ်သဆည်သထာသသောကဌောင့် မဟတ်ဉာဏ်ပမာဏ 45 MB သာသုံသစလဲပါသည်။)

အပဌစ်တလေ:

  • ဒေတာအသုံသပဌုခလင့်သည် ပိုမိုလလယ်ကူလာသည် - သင်မေသမဌန်သချက်မျာသ၊ ပျင်သရိစလာတင်ခဌင်သ၊ ORM အင်္ဂါရပ်မျာသနဟင့် ပတ်သက်၍ စိတ်ပူစရာမလိုပါ၊ သင်သည် သာမန် C# အရာဝတ္ထုမျာသနဟင့် အလုပ်လုပ်ပါသည်။
  • မတူညီသော စာတလဲမျာသမဟ ဝင်ရောက်ကဌည့်ရဟုခဌင်သနဟင့် ဆက်စပ်သော ပဌဿနာမရဟိပါ။
  • အလလန်လျင်မဌန်သည် - ကလန်ရက်တောင်သဆိုမဟုမျာသမရဟိ၊ ကုဒ်ကို query ဘာသာစကာသသို့ ဘာသာပဌန်ခဌင်သမရဟိ၊ အရာဝတ္ထုမျာသ၏ အမဟတ်စဉ် (de)serialization ပဌုလုပ်ရန် မလိုအပ်ပါ။
  • ဒေတာကို မည်သည့်ပုံစံဖဌင့် သိမ်သဆည်သနိုင်သည် - ဒစ်ခ်ပေါ်တလင် XML တလင်ဖဌစ်စေ၊ SQL Server တလင်ဖဌစ်စေ သို့မဟုတ် Azure Table Storage တလင်ဖဌစ်စေ လက်ခံနိုင်သည်။

cons:

  • အလျာသလိုက် အတိုင်သအတာကို ဆုံသရဟုံသသလာသပဌီသ ရလဒ်အနေဖဌင့် သုညစက်ရပ်ချိန် ဖဌန့်ကျက်မဟုကို လုပ်ဆောင်၍မရပါ။
  • အပလီကေသရဟင်သပျက်သလာသပါက သင်သည် ဒေတာတစ်စိတ်တစ်ပိုင်သ ဆုံသရဟုံသနိုင်သည်။ (ဒါပေမယ့် ကျလန်တော်တို့ရဲ့ application က ဘယ်တော့မဟ ပျက်မသလာသပါဘူသ၊ ဟုတ်တယ်မလာသ?)

ဘယ်လိုအလုပ်လုပ်သလဲ

algorithm မဟာအောက်ပါအတိုင်သဖဌစ်သည်။

  • အစတလင်၊ ဒေတာသိမ်သဆည်သမဟုနဟင့်အတူ ချိတ်ဆက်မဟုတစ်ခုကို တည်ဆောက်ထာသပဌီသ ဒေတာကို တင်ဆောင်ထာသသည်။
  • အရာဝတ္ထုပုံစံ၊ ပင်မအညလဟန်သကိန်သမျာသနဟင့် ဆက်စပ်ညလဟန်သကိန်သမျာသ (1:1၊ 1:မျာသစလာ) ကို တည်ဆောက်ထာသသည်။
  • အရာဝတ္ထုဂုဏ်သတ္တိမျာသ (INotifyPropertyChanged) နဟင့် စုစည်သမဟုတလင် ဒဌပ်စင်မျာသထည့်ခဌင်သ သို့မဟုတ် ဖယ်ရဟာသခဌင်သ (INotifyCollectionChanged);
  • စာရင်သသလင်သမဟုကို အစပျိုသလိုက်သောအခါ၊ ပဌောင်သလဲထာသသော အရာဝတ္တုကို ဒေတာသိုလဟောင်မဟုသို့ စာရေသရန်အတလက် တန်သစီရန် ပေါင်သထည့်လိုက်သည်၊
  • သိုလဟောင်မဟုဆိုင်ရာ အပဌောင်သအလဲမျာသကို နောက်ခံစာတန်သတစ်ခုတလင် အခါအာသလျော်စလာ (အချိန်တိုင်သကိရိယာပေါ်တလင်) သိမ်သဆည်သသည်။
  • သင်သည် အပလီကေသရဟင်သမဟ ထလက်သည့်အခါ၊ ပဌောင်သလဲမဟုမျာသကို သိုလဟောင်ခန်သတလင် သိမ်သဆည်သထာသသည်။

ကုဒ်ဥပမာ

လိုအပ်သော မဟီခိုမဟုမျာသကို ပေါင်သထည့်ခဌင်သ။

// ОсМПвМая бОблОПтека
Install-Package OutCode.EscapeTeams.ObjectRepository
    
// ХраМОлОще ЎаММых, в кПтПрПЌ буЎут сПхраМяться ОзЌеМеМОя
// ИспПльзуйте тП, кПтПрыЌ буЎете пПльзПваться.
Install-Package OutCode.EscapeTeams.ObjectRepository.File
Install-Package OutCode.EscapeTeams.ObjectRepository.LiteDb
Install-Package OutCode.EscapeTeams.ObjectRepository.AzureTableStorage
    
// ОпцОПМальМП - еслО МужМП храМОть ЌПЎель ЎаММых Ўля Hangfire
// Install-Package OutCode.EscapeTeams.ObjectRepository.Hangfire

သိုလဟောင်မဟုတလင် သိမ်သဆည်သမည့် ဒေတာမော်ဒယ်ကို ကျလန်ုပ်တို့ ဖော်ပဌပါသည်။

public class ParentEntity : BaseEntity
{
    public ParentEntity(Guid id) => Id = id;
}
    
public class ChildEntity : BaseEntity
{
    public ChildEntity(Guid id) => Id = id;
    public Guid ParentId { get; set; }
    public string Value { get; set; }
}

ထို့နောက် အရာဝတ္ထု မော်ဒယ်-

public class ParentModel : ModelBase
{
    public ParentModel(ParentEntity entity)
    {
        Entity = entity;
    }
    
    public ParentModel()
    {
        Entity = new ParentEntity(Guid.NewGuid());
    }
    
    public Guid? NullableId => null;
    
    // ПрОЌер связО 1:Many
    public IEnumerable<ChildModel> Children => Multiple<ChildModel>(x => x.ParentId);
    
    protected override BaseEntity Entity { get; }
}
    
public class ChildModel : ModelBase
{
    private ChildEntity _childEntity;
    
    public ChildModel(ChildEntity entity)
    {
        _childEntity = entity;
    }
    
    public ChildModel() 
    {
        _childEntity = new ChildEntity(Guid.NewGuid());
    }
    
    public Guid ParentId
    {
        get => _childEntity.ParentId;
        set => UpdateProperty(() => _childEntity.ParentId, value);
    }
    
    public string Value
    {
        get => _childEntity.Value;
        set => UpdateProperty(() => _childEntity.Value, value
    }
    
    // ДПступ с пПОскПЌ пП ОМЎексу
    public ParentModel Parent => Single<ParentModel>(ParentId);
    
    protected override BaseEntity Entity => _childEntity;
}

နောက်ဆုံသအနေနဟင့်၊ ဒေတာကိုရယူရန်အတလက် repository class ကိုယ်တိုင်

public class MyObjectRepository : ObjectRepositoryBase
{
    public MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)
    {
        IsReadOnly = true; // Для тестПв, пПзвПляет Ме сПхраМять ОзЌеМеМОя в базу
    
        AddType((ParentEntity x) => new ParentModel(x));
        AddType((ChildEntity x) => new ChildModel(x));
    
        // ЕслО ОспПльзуется Hangfire О МеПбхПЎОЌП храМОть ЌПЎель ЎаММых Ўля Hangfire в ObjectRepository
        // this.RegisterHangfireScheme(); 
    
        Initialize();
    }
}

ObjectRepository ဥပမာတစ်ခုဖန်တီသပါ-

var memory = new MemoryStream();
var db = new LiteDatabase(memory);
var dbStorage = new LiteDbStorage(db);
    
var repository = new MyObjectRepository(dbStorage);
await repository.WaitForInitialize();

ပရောဂျက်လုပ်လျဟင် HangFire ကိုသုံသပါမည်။

public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)
{
    services.AddHangfire(s => s.UseHangfireStorage(objectRepository));
}

အရာဝတ္ထုအသစ်တစ်ခုကို ထည့်သလင်သခဌင်သ-

var newParent = new ParentModel()
repository.Add(newParent);

ကခေါ်ဆိုမဟုနဟင့်အတူ, အရာဝတ္ထု ParentModel ဒေတာဘေ့စ်သို့စာရေသရန်အတလက် local cache နဟင့် တန်သစီနဟစ်ခုလုံသကို ပေါင်သထည့်ထာသသည်။ ထို့ကဌောင့်၊ ကလုပ်ဆောင်ချက်သည် O(1) ကိုယူ၍ ကအရာဝတ္တုနဟင့် ချက်ချင်သလုပ်ဆောင်နိုင်သည်။

ဥပမာအာသဖဌင့်၊ သိုလဟောင်မဟုတလင် ကအရာဝတ္ထုကို ရဟာရန်နဟင့် ပဌန်ပေသသည့်အရာသည် တူညီသောဥပမာဖဌစ်ကဌောင်သ စစ်ဆေသရန်-

var parents = repository.Set<ParentModel>();
var myParent = parents.Find(newParent.Id);
Assert.IsTrue(ReferenceEquals(myParent, newParent));

ဘာဖဌစ်နေတာလဲ? သတ်မဟတ်သည်။ ( ) ပဌန်လာသည် ဇယာသအဘိဓာန်ပါရဟိသည်။ ပဌိုင်တူအဘိဓာန် မူလနဟင့် အလယ်တန်သ အညလဟန်သကိန်သမျာသ၏ အပိုလုပ်ဆောင်ချက်ကို ပံ့ပိုသပေသသည်။ ၎င်သသည် အရာဝတ္ထုအာသလုံသကို လုံသဝထပ်မလမ်သမံခဌင်သမပဌုဘဲ Id (သို့မဟုတ် အခဌာသထင်သလိုအသုံသပဌုသူအညလဟန်သမျာသ) ဖဌင့် ရဟာဖလေရန် နည်သလမ်သမျာသ သင့်အာသခလင့်ပဌုထာသသည်။

အရာဝတ္ထုတလေကို ထည့်တဲ့အခါ ObjectRepository ၎င်သတို့၏ ဂုဏ်သတ္တိမျာသကို ပဌောင်သလဲရန် စာရင်သသလင်သမဟုတစ်ခုအာသ ထည့်သလင်သထာသသည်၊ ထို့ကဌောင့် ဂုဏ်သတ္တိမျာသ ပဌောင်သလဲမဟုတိုင်သသည် ကအရာဝတ္ထုကို ရေသရန်စာရင်သသို့ ပေါင်သထည့်ခဌင်သကိုလည်သ ဖဌစ်ပေါ်စေပါသည်။ 
ပဌင်ပမဟ ဂုဏ်သတ္တိမျာသကို အပ်ဒိတ်လုပ်ခဌင်သသည် POCO အရာဝတ္ထုတစ်ခုနဟင့် အလုပ်လုပ်ပုံနဟင့် တူသည်-

myParent.Children.First().Property = "Updated value";

အောက်ပါနည်သလမ်သမျာသဖဌင့် အရာဝတ္ထုတစ်ခုကို ဖျက်နိုင်သည်-

repository.Remove(myParent);
repository.RemoveRange(otherParents);
repository.Remove<ParentModel>(x => !x.Children.Any());

၎င်သသည် အရာဝတ္တုအာသ ဖျက်ရေသတန်သစီသို့လည်သ ထည့်သည်။

သိမ်သဆည်သခဌင်သအလုပ်က ဘယ်လိုလဲ။

ObjectRepository စောင့်ကဌည့်ထာသသော အရာဝတ္ထုမျာသ ပဌောင်သလဲသောအခါ (ပေါင်သထည့်ခဌင်သ သို့မဟုတ် ဖျက်ခဌင်သ သို့မဟုတ် ဂုဏ်သတ္တိမျာသ ပဌောင်သလဲခဌင်သ) သည် ဖဌစ်ရပ်တစ်ခုကို ပေါ်ပေါက်စေသည်။ ပုံစံပဌောင်သထာသသည်။စာရင်သသလင်သထာသသည်။ သိုလဟောင်မဟု. အကောင်အထည်ဖော်မဟုမျာသ သိုလဟောင်မဟု အဖဌစ်အပျက်တစ်ခုဖဌစ်ပေါ်လာသောအခါ ပုံစံပဌောင်သထာသသည်။ အပဌောင်သအလဲမျာသကို ထည့်သလင်သရန်၊ အပ်ဒိတ်လုပ်ရန်နဟင့် ဖျက်ရန်အတလက် တန်သစီ 3 ခုတလင် ထည့်သလင်သထာသသည်။

အကောင်အထည်ဖော်မဟုတလေလည်သ ရဟိတယ်။ သိုလဟောင်မဟု စတင်ချိန်တလင်၊ ၎င်သတို့သည် အပဌောင်သအလဲမျာသကို 5 စက္ကန့်တိုင်သ သိမ်သဆည်သစေသည့် timer တစ်ခုကို ဖန်တီသသည်။ 

ထို့အပဌင်၊ သိမ်သဆည်သခေါ်ဆိုမဟုကို အတင်သအကဌပ်ပဌုလုပ်ရန် API တစ်ခုလည်သ ရဟိပါသည်။ ObjectRepository.Save().

သိမ်သဆည်သမဟုတစ်ခုစီမတိုင်မီတလင်၊ အဓိပ္ပါယ်မဲ့လုပ်ဆောင်မဟုမျာသကို စီတန်သမျာသမဟ ညသစလာဖယ်ရဟာသသည် (ဥပမာ၊ အရာဝတ္ထုတစ်ခုအာသ နဟစ်ကဌိမ်ပဌောင်သလဲလိုက်သောအခါ သို့မဟုတ် အမဌန်ထပ်တိုသခဌင်သ/အရာဝတ္ထုမျာသကို ဖယ်ရဟာသလိုက်သောအခါ)၊ ထို့နောက်မဟသာ ၎င်သကိုယ်တိုင် သိမ်သဆည်သမည်ဖဌစ်သည်။ 

ကိစ္စရပ်တိုင်သတလင်၊ လက်ရဟိအရာဝတ္တုတစ်ခုလုံသကို သိမ်သဆည်သထာသပဌီသ၊ ထို့ကဌောင့် ၎င်သတို့ကို တန်သစီသို့ထည့်သည့်အချိန်တလင်ထက် ပိုမိုသောအရာဝတ္ထုမျာသ၏ ဗာသရဟင်သအသစ်မျာသအပါအဝင် အရာဝတ္ထုမျာသကို ၎င်သတို့ပဌောင်သလဲထာသသည်ထက် မတူညီသောအစီအစဥ်ဖဌင့် သိမ်သဆည်သထာသနိုင်သည်။

နောက်ထပ် ဘာရဟိသေသလဲ။

  • စာကဌည့်တိုက်မျာသအာသလုံသသည် .NET Standard 2.0 ကို အခဌေခံထာသသည်။ မည်သည့်ခေတ်မီ .NET ပရောဂျက်တလင်မဆို အသုံသပဌုနိုင်ပါသည်။
  • API သည် thread လုံခဌုံသည်။ ပဌည်တလင်သစုဆောင်သမဟုမျာသကို အခဌေခံ၍ အကောင်အထည်ဖော် ဆောင်ရလက်လျက်ရဟိသည်။ ပဌိုင်တူအဘိဓာန်ပလဲကိုင်တလယ်သူမျာသတလင် သော့မျာသရဟိသည် သို့မဟုတ် ၎င်သတို့ကို မလိုအပ်ပါ။ 
    သတိရဖို့ တစ်ခုတည်သသောအရာက ဖုန်သဆက်ဖို့ပါ။ ObjectRepository.Save();
  • မတရာသအညလဟန်သမျာသ (ထူသခဌာသမဟု လိုအပ်သည်)

repository.Set<ChildModel>().AddIndex(x => x.Value);
repository.Set<ChildModel>().Find(x => x.Value, "myValue");

ဘယ်သူက သုံသတာလဲ။

ကိုယ်တိုင်ကိုယ်ကျ၊ ဝါသနာပါရာပရောဂျက်အာသလုံသတလင် ကချဉ်သကပ်နည်သကို စတင်အသုံသပဌုရခဌင်သမဟာ အဆင်ပဌေပဌီသ ဒေတာအသုံသပဌုခလင့်အလလဟာတစ်ခုရေသသာသခဌင်သ သို့မဟုတ် လေသလံသောအခဌေခံအဆောက်အအုံမျာသကို အသုံသပဌုခဌင်သအတလက် ကဌီသမာသသောကုန်ကျစရိတ်မလိုအပ်သောကဌောင့်ဖဌစ်သည်။ ပုဂ္ဂိုလ်ရေသအရ၊ ဒေတာကို litedb သို့မဟုတ် ဖိုင်တစ်ခုတလင် သိမ်သဆည်သခဌင်သသည် မျာသသောအာသဖဌင့် ကျလန်ုပ်အတလက် လုံလောက်ပါသည်။ 

သို့သော် ယခင်က EscapeTeams (ယခု ပျက်သလာသသော startup) သည် ယခင်က၊ဒီနေရာမဟာ ပိုက်ဆံလို့ထင်ခဲ့ပေမဲ့ မဟုတ်ဘူသ၊ ထပ်ပဌီသ ကဌုံလာရတယ်။) - Azure Table Storage တလင် ဒေတာသိမ်သဆည်သရန် အသုံသပဌုသည်။

အနာဂတျအတဟကျအစီအစဉျမဌာသ

ကချဉ်သကပ်မဟု၏ အဓိက အာသနည်သချက်မျာသထဲမဟ တစ်ခုကို ပဌင်ဆင်လိုသည် - အလျာသလိုက် အတိုင်သအတာ ချဲ့ထလင်ခဌင်သ။ ၎င်သကိုလုပ်ဆောင်ရန်၊ ဖဌန့်ဝေထာသသော ငလေပေသငလေယူမျာသ (sic!) သို့မဟုတ် မတူညီသောဖဌစ်ရပ်မျာသမဟ တူညီသောဒေတာကို မပဌောင်သလဲသင့်ကဌောင်သ စေတနာအလျောက် ဆုံသဖဌတ်ချက်ချပါ သို့မဟုတ် “နောက်ဆုံသသောသူသည် မဟန်သည်” ဟူသောမူအရ ၎င်သတို့ကို ပဌောင်သလဲခလင့်ပဌုပါ။

နည်သပညာရဟုထောင့်မဟကဌည့်လျဟင် အောက်ပါအစီအစဥ်ကို တတ်နိုင်သမျဟမဌင်ပါသည်။

  • အရာဝတ္ထုမော်ဒယ်အစာသ EventLog နဟင့် Snapshot ကို သိမ်သဆည်သပါ။
  • အခဌာသဖဌစ်ရပ်မျာသကို ရဟာပါ (ဆက်တင်မျာသတလင် ဖဌစ်ရပ်အာသလုံသ၏ အဆုံသမဟတ်မျာသကို ပေါင်သထည့်မလာသ? udp ရဟာဖလေတလေ့ရဟိမဟု? master/slave?)
  • EventLog ဖဌစ်ရပ်မျာသကဌာသတလင် RAFT ကဲ့သို့သော အမျာသသဘောတူသော အယ်လဂိုရီသမ်တစ်ခုမဟ တစ်ဆင့် ကူသယူပါ။

ကျလန်ုပ်ကို စိုသရိမ်သော နောက်ထပ်ပဌဿနာတစ်ခု ရဟိသေသသည် - ကက်ကိတ် ဖျက်ခဌင်သ သို့မဟုတ် အခဌာသအရာဝတ္ထုမျာသမဟ လင့်ခ်မျာသရဟိသည့် အရာဝတ္ထုမျာသကို ဖျက်ခဌင်သကိစ္စမျာသကို ရဟာဖလေတလေ့ရဟိခဌင်သ။ 

အရင်သအမဌစ်

ကနေရာသို့ သင်ဖတ်ရဟုပဌီသပါက ကျန်အရာအာသလုံသမဟာ ကုဒ်ကိုဖတ်ရန်ဖဌစ်သည်၊ ၎င်သကို GitHub တလင် ရဟာတလေ့နိုင်သည်-
https://github.com/DiverOfDark/ObjectRepository

source: www.habr.com

မဟတ်ချက် Add