ObjectRepository - .NET ستاسو د کور پروژو لپاره په حافظه کې د ذخیره کولو نمونه

ولې ټول معلومات په حافظه کې ساتئ؟

د سایټ یا بیکنډ ډیټا ذخیره کولو لپاره ، د ډیری هوښیار خلکو لومړۍ هیله به د SQL ډیټابیس غوره کول وي. 

مګر ځینې وختونه فکر ذهن ته راځي چې د ډیټا ماډل د SQL لپاره مناسب ندي: د مثال په توګه ، کله چې د لټون یا ټولنیز ګراف رامینځته کول ، تاسو اړتیا لرئ د شیانو ترمینځ پیچلي اړیکو لټون وکړئ. 

ترټولو بد حالت هغه وخت دی چې تاسو په ټیم کې کار کوئ او یو همکار نه پوهیږي چې څنګه د چټک پوښتنو رامینځته کول. تاسو څومره وخت د N+1 ستونزو حل کولو او د اضافي شاخصونو په جوړولو کې مصرف کړی ترڅو په اصلي پاڼه کې انتخاب په مناسب وخت کې بشپړ شي؟

بله مشهوره طریقه NoSQL ده. څو کاله دمخه د دې موضوع په اړه خورا ډیر هیپ شتون درلود - د هر مناسب فرصت لپاره دوی مونګو ډی بی ځای په ځای کړی او د json اسنادو په بڼه د ځوابونو څخه خوښ وو. (په لاره کې، تاسو باید په اسنادو کې د سرکلر لینکونو له امله څومره کرچونه داخل کړئ؟).

زه د بل ، بدیل میتود هڅه کولو وړاندیز کوم - ولې د غوښتنلیک حافظه کې ټول ډیټا ذخیره کولو هڅه مه کوئ ، په وخت سره یې په تصادفي ذخیره (فایل ، ریموټ ډیټابیس) کې خوندي کړئ؟ 

حافظه ارزانه شوې، او د ډیری کوچنیو او منځنیو پروژو لپاره هر ډول احتمالي ډاټا به د 1 GB حافظې کې فټ شي. (د مثال په توګه، زما د خوښې کور پروژه ده مالي تعقیبونکی، کوم چې د یو نیم کال لپاره زما د لګښتونو ، بیلانسونو او راکړو ورکړو ورځني احصایې او تاریخ ساتي ، یوازې 45 MB حافظه مصرفوي.)

پرو:

  • ډیټا ته لاسرسی اسانه کیږي - تاسو اړتیا نلرئ د پوښتنو ، سست بار کولو ، ORM ځانګړتیاو په اړه اندیښنه ولرئ ، تاسو د عادي C# شیانو سره کار کوئ؛
  • د مختلفو تارونو څخه د لاسرسي سره تړلې ستونزې شتون نلري؛
  • خورا ګړندی - د شبکې غوښتنې نه ، د پوښتنې ژبې ته د کوډ ژباړه نه ، د شیانو سیریل کولو ته اړتیا نشته؛
  • دا د منلو وړ ده چې ډاټا په هر شکل کې ذخیره کړئ - که دا په ډیسک کې XML کې وي، یا په SQL سرور کې، یا د Azure Table Storage کې.

ضمیمه:

  • افقی پیمانه ورکه شوې، او په پایله کې، د صفر د وخت ضایع کول نشي ترسره کیدی؛
  • که غوښتنلیک خراب شي، تاسو ممکن یو څه ډاټا له لاسه ورکړئ. (مګر زموږ غوښتنلیک هیڅکله نه خرابیږي، سمه ده؟)

دا څنګه کار کوي؟

الګوریتم په لاندې ډول دی:

  • په پیل کې ، د ډیټا ذخیره کولو سره اړیکه رامینځته کیږي ، او ډیټا باریږي؛
  • د اعتراض ماډل، لومړني شاخصونه، او اړونده شاخصونه (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;
}

او په نهایت کې ، ډیټا ته د لاسرسي لپاره پخپله د ذخیره کولو ټولګي:

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);

د دې غږ سره، اعتراض د پلار ماډل ډیټابیس ته د لیکلو لپاره ځایی کیچ او کتار دواړو ته اضافه کیږي. له همدې امله، دا عملیات 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 کله چې څارل شوي توکي بدل شي (یا اضافه کول یا حذف کول ، یا د ملکیت بدلول) ، پیښه راپورته کوي ماډل بدل شویګډون کړی ISstorage. تطبیقات ISstorage کله چې پیښه رامنځته شي ماډل بدل شوی بدلونونه په 3 کتارونو کې اچول کیږي - د اضافه کولو، تازه کولو او حذف کولو لپاره.

همدارنګه تطبیقونه ISstorage په پیل کولو سره، دوی یو ټایمر رامینځته کوي چې بدلونونه په هر 5 ثانیو کې خوندي کیږي. 

سربیره پردې ، د خوندي کولو تلیفون مجبورولو لپاره API شتون لري: ObjectRepository.Save().

د هر خوندي کولو دمخه، بې معنی عملیات لومړی د قطارونو څخه لیرې شوي (د بیلګې په توګه، نقل شوي پیښې - کله چې یو شی دوه ځله بدل شوی یا په چټکۍ سره اضافه شوي / حذف شوي شیان)، او یوازې بیا ځان خوندي کوي. 

په ټولو قضیو کې، ټول اوسنی څیز خوندي شوی، نو دا ممکنه ده چې شیان د بدلون په پرتله په بل ترتیب کې خوندي شوي وي، په شمول د شیانو نوې نسخې د هغه وخت په پرتله چې دوی په قطار کې اضافه شوي.

نور څه شته؟

  • ټول کتابتونونه د .NET معیاري 2.0 پر بنسټ دي. په هره عصري .NET پروژه کې کارول کیدی شي.
  • API تار خوندي دی. د داخلي راټولولو پر بنسټ پلي کیږي متقابل قاموسد پیښې سمبالونکي یا لاکونه لري یا ورته اړتیا نلري. 
    یوازینی شی چې د یادولو وړ دی تلیفون کول دي ObjectRepository.Save();
  • خپلمنځي شاخصونه (ځانګړتیا ته اړتیا لري):

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

څوک یې کاروي؟

په شخصي توګه، ما د دې طریقې کارول په ټولو شوق پروژو کې پیل کړل ځکه چې دا اسانه ده او د معلوماتو لاسرسي پرت لیکلو یا د درنو زیربناوو ځای پرځای کولو لپاره لوی لګښتونو ته اړتیا نلري. په شخصي توګه، په litedb یا فایل کې د معلوماتو ذخیره کول معمولا زما لپاره کافي دي. 

مګر په تیرو کې، کله چې اوس ناکاره شوي پیل شوي EscapeTeems (ما فکر کاوه چې دا دلته ده، پیسې - مګر نه، بیا تجربه) - د Azure Table Storage کې د معلوماتو ذخیره کولو لپاره کارول کیږي.

د راتلونکي لپاره پلانونه

زه غواړم د دې تګلارې یو له اصلي زیانونو څخه حل کړم - افقی اندازه کول. د دې کولو لپاره، تاسو یا توزیع شوي لیږد ته اړتیا لرئ (sic!)، یا د قوي ارادې پریکړه وکړئ چې د مختلفو مثالونو څخه ورته ډاټا باید بدلون ونلري، یا اجازه ورکړي چې د اصولو سره سم بدلون ومومي "څوک چې وروستی دی سم دی."

د تخنیکي نظر څخه، زه د امکان تر حده لاندې سکیم ګورم:

  • د اعتراض ماډل پرځای د EventLog او سنیپ شاټ ذخیره کړئ
  • نور مثالونه ومومئ (په ترتیباتو کې د ټولو مثالونو پای ټکي اضافه کړئ؟ udp کشف؟ ماسټر / غلام؟)
  • د هر توافق الګوریتم له لارې د EventLog مثالونو ترمنځ نقل کول، لکه RAFT.

دلته بله ستونزه هم شتون لري چې ما اندیښمن کوي ​​- د cascade حذف کول، یا د شیانو د حذف کولو قضیې کشف کول چې د نورو شیانو سره اړیکې لري. 

سرچینه

که تاسو دلته ټولې لارې لوستلې وي، نو ټول هغه څه چې پاتې دي د کوډ لوستل دي؛ دا په GitHub کې موندل کیدی شي:
https://github.com/DiverOfDark/ObjectRepository

سرچینه: www.habr.com

Add a comment