ObjectRepository - .NET ان-ميموري مخزن جو نمونو توھان جي گھر جي منصوبن لاءِ

ميموري ۾ سموري ڊيٽا کي ڇو محفوظ ڪيو وڃي؟

ويب سائيٽ يا پس منظر واري ڊيٽا کي ذخيرو ڪرڻ لاء، سڀ کان وڌيڪ سمجھدار ماڻهن جي پهرين خواهش هڪ SQL ڊيٽابيس کي چونڊڻ لاء هوندي. 

پر ڪڏهن ڪڏهن ذهن ۾ اهو خيال اچي ٿو ته ڊيٽا ماڊل SQL لاءِ موزون ناهي: مثال طور، جڏهن ڳولا يا سماجي گراف ٺاهي رهيا آهيو، توهان کي شين جي وچ ۾ پيچيده رشتا ڳولڻ جي ضرورت آهي. 

بدترين صورتحال اها آهي جڏهن توهان هڪ ٽيم ۾ ڪم ڪري رهيا آهيو ۽ هڪ ساٿي کي خبر ناهي ته جلدي سوالن کي ڪيئن ٺاهيو. توهان ڪيترو وقت خرچ ڪيو N+1 مسئلن کي حل ڪرڻ ۽ اضافي انڊيڪس ٺاهڻ ۾ ته جيئن مکيه صفحي تي SELECT مناسب وقت ۾ مڪمل ٿئي؟

ٻيو مشهور طريقو آهي NoSQL. ڪيترائي سال اڳ هن موضوع جي چوڌاري تمام گهڻو هائپ هو - ڪنهن به آسان موقعي لاءِ انهن مونگو ڊي بي کي ترتيب ڏنو ۽ json دستاويزن جي صورت ۾ جوابن سان خوش هئا (انهي سان، توهان کي دستاويزن ۾ سرڪيولر لنڪس جي ڪري ڪيترا ڪچيون داخل ڪرڻ گهرجن؟).

مان صلاح ڏيان ٿو هڪ ٻيو، متبادل طريقو - ڇو نه ڪوشش ڪريو سموري ڊيٽا کي ايپليڪيشن ميموري ۾ محفوظ ڪرڻ، وقتي طور تي ان کي بي ترتيب اسٽوريج (فائل، ريموٽ ڊيٽابيس) ۾ محفوظ ڪرڻ؟ 

ياداشت سستي ٿي چڪي آهي، ۽ تمام ننڍي ۽ وچولي سائيز جي منصوبن لاءِ ڪنهن به ممڪن ڊيٽا 1 GB ميموري ۾ فٽ ٿيندي. (مثال طور، منهنجو پسنديده گهر منصوبو آهي مالي ٽريڪٽر، جيڪو روزاني انگ اکر ۽ منهنجي خرچن، بيلنس ۽ ٽرانزيڪشن جي تاريخ کي هڪ اڌ سال تائين رکي ٿو، صرف 45 MB ميموري استعمال ڪري ٿو.)

پرو:

  • ڊيٽا تائين رسائي آسان ٿي ويندي آهي - توهان کي سوالن جي باري ۾ پريشان ٿيڻ جي ضرورت ناهي، سست لوڊ ڪرڻ، ORM خاصيتون، توهان عام C# شين سان ڪم ڪريو ٿا؛
  • مختلف موضوعن جي رسائي سان لاڳاپيل ڪو مسئلو ناهي؛
  • تمام تيز - نه نيٽ ورڪ درخواستون، ڪو به ڪوڊ جو ترجمو ٻوليءَ ۾ نه، ڪا ضرورت ناهي (ڊي) شين جي سيريلائيزيشن؛
  • اهو قابل قبول آهي ڊيٽا کي ڪنهن به شڪل ۾ ذخيرو ڪرڻ لاءِ- چاهي اهو ڊسڪ تي XML ۾ هجي، يا SQL سرور ۾، يا Azure Table Storage ۾.

ڪن

  • افقي اسڪيلنگ گم ٿي وئي آهي، ۽ نتيجي طور، صفر ختم ٿيڻ جي مقرري نه ٿي ڪري سگهجي؛
  • جيڪڏهن ايپليڪيشن حادثو ٿئي ٿي، توهان جزوي طور ڊيٽا وڃائي سگهو ٿا. (پر اسان جي ايپليڪيشن ڪڏهن به تباهه نه ٿيندي، صحيح؟)

ان کي ڪيئن ڪم ڪندو؟

الگورتھم ھي as ڏنل آھي:

  • شروعات ۾، ڊيٽا اسٽوريج سان هڪ ڪنيڪشن قائم ڪيو ويو آهي، ۽ ڊيٽا لوڊ ٿيل آهي؛
  • هڪ اعتراض ماڊل، پرائمري انڊيڪسس، ۽ لاڳاپا انڊيڪس (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 جڏهن مانيٽر ٿيل شيون تبديل ٿين ٿيون (يا ته شامل ڪرڻ يا حذف ڪرڻ، يا ملڪيت تبديل ڪرڻ)، هڪ واقعو وڌائي ٿو ماڊل تبديل ٿيلجي رڪنيت حاصل ڪئي اسٽوريج. لاڳو ڪرڻ اسٽوريج جڏهن ڪو واقعو ٿئي ٿو ماڊل تبديل ٿيل تبديليون 3 قطارن ۾ رکيل آھن - شامل ڪرڻ، تازه ڪاري ڪرڻ ۽ حذف ڪرڻ لاءِ.

پڻ عملدرآمد اسٽوريج شروعات ڪرڻ تي، اهي هڪ ٽائمر ٺاهيندا آهن جيڪي هر 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 يا فائل ۾ ڊيٽا محفوظ ڪرڻ عام طور تي مون لاء ڪافي آهي. 

پر ماضي ۾، جڏهن هاڻي ختم ٿيل شروعاتي EscapeTeams (مون سوچيو ته هتي اهو آهي، پئسا - پر نه، ٻيهر تجربو) - Azure ٽيبل اسٽوريج ۾ ڊيٽا کي ذخيرو ڪرڻ لاء استعمال ڪيو ويو.

مستقبل لاء منصوبا

مان هن طريقي جي مکيه نقصانن مان هڪ کي درست ڪرڻ چاهيان ٿو - افقي اسڪيلنگ. ائين ڪرڻ لاءِ، توهان کي ضرورت آهي يا ته ورهايل ٽرانزيڪشن (sic!)، يا هڪ مضبوط ارادي سان فيصلو ڪيو وڃي ته مختلف مثالن مان هڪجهڙو ڊيٽا تبديل نه ٿيڻ گهرجي، يا انهن کي اصول مطابق تبديل ڪرڻ ڏيو ”جيڪو آخري آهي صحيح آهي“.

ٽيڪنيڪل نقطي نظر کان، مون کي هيٺ ڏنل اسڪيم ممڪن طور تي نظر اچي ٿو:

  • اسٽور ايونٽ لاگ ۽ سنيپ شاٽ بدران اعتراض ماڊل
  • ٻيا مثال ڳولھيو (سڀني مثالن جا آخري پوائنٽ سيٽنگن ۾ شامل ڪريو؟ udp دريافت؟ ماسٽر/غلام؟)
  • ڪنهن به اتفاق الورورٿم ذريعي EventLog مثالن جي وچ ۾ نقل ڪريو، جهڙوڪ RAFT.

هتي هڪ ٻيو مسئلو پڻ آهي جيڪو مون کي پريشان ڪري ٿو - cascade حذف ڪرڻ، يا شين جي حذف ٿيڻ جي ڪيسن جي نشاندهي ڪرڻ جيڪي ٻين شين مان لنڪ آهن. 

ذريعو

جيڪڏهن توهان پڙهي ورتو آهي هتي جو سڄو رستو، پوءِ باقي رهي ٿو ڪوڊ پڙهڻ لاءِ؛ اهو ڳولهي سگهجي ٿو GitHub:
https://github.com/DiverOfDark/ObjectRepository

جو ذريعو: www.habr.com

تبصرو شامل ڪريو