ObjectRepository - .NET muundo wa hifadhi ya kumbukumbu kwa miradi yako ya nyumbani

Kwa nini uhifadhi data zote kwenye kumbukumbu?

Ili kuhifadhi tovuti au data ya nyuma, hamu ya kwanza ya watu wengi wenye akili timamu itakuwa kuchagua hifadhidata ya SQL. 

Lakini wakati mwingine mawazo huja akilini kwamba mfano wa data haifai kwa SQL: kwa mfano, wakati wa kujenga utafutaji au grafu ya kijamii, unahitaji kutafuta mahusiano magumu kati ya vitu. 

Hali mbaya zaidi ni wakati unafanya kazi katika timu na mwenzako hajui jinsi ya kuunda maswali ya haraka. Je, ulitumia muda gani kutatua matatizo ya N+1 na kutengeneza faharasa za ziada ili SELECT kwenye ukurasa mkuu ukamilike kwa muda ufaao?

Njia nyingine maarufu ni NoSQL. Miaka kadhaa iliyopita kulikuwa na hype nyingi karibu na mada hii - kwa hafla yoyote inayofaa walipeleka MongoDB na walifurahiya majibu katika mfumo wa hati za json. (kwa njia, ulilazimika kuingiza viboko ngapi kwa sababu ya viungo vya mviringo kwenye hati?).

Ninapendekeza kujaribu njia nyingine, mbadala - kwa nini usijaribu kuhifadhi data zote kwenye kumbukumbu ya programu, mara kwa mara uihifadhi kwa uhifadhi wa nasibu (faili, hifadhidata ya mbali)? 

Kumbukumbu imekuwa nafuu, na data yoyote inayowezekana kwa miradi mingi midogo na ya kati itatoshea kwenye kumbukumbu ya GB 1. (Kwa mfano, mradi wangu wa nyumbani unaopenda ni mfuatiliaji wa fedha, ambayo huhifadhi takwimu za kila siku na historia ya gharama, salio, na miamala yangu kwa mwaka mmoja na nusu, hutumia kumbukumbu ya MB 45 pekee.)

Faida:

  • Ufikiaji wa data unakuwa rahisi - huna haja ya kuwa na wasiwasi kuhusu maswali, upakiaji wa uvivu, vipengele vya ORM, unafanya kazi na vitu vya kawaida vya C #;
  • Hakuna matatizo yanayohusiana na upatikanaji kutoka kwa nyuzi tofauti;
  • Haraka sana - hakuna maombi ya mtandao, hakuna tafsiri ya msimbo katika lugha ya swala, hakuna haja ya (de) serialization ya vitu;
  • Inakubalika kuhifadhi data kwa namna yoyote - iwe katika XML kwenye diski, au katika Seva ya SQL, au kwenye Hifadhi ya Jedwali la Azure.

Minus:

  • Upeo wa usawa unapotea, na kwa sababu hiyo, uwekaji wa muda wa sifuri hauwezi kufanywa;
  • Ikiwa programu itaacha kufanya kazi, unaweza kupoteza data kwa kiasi. (Lakini maombi yetu kamwe hayashindwi, sivyo?)

Jinsi gani kazi?

Algorithm ni kama ifuatavyo:

  • Mwanzoni, uunganisho umeanzishwa na hifadhi ya data, na data imefungwa;
  • Kielelezo cha kitu, faharasa za msingi, na faharasa za uhusiano (1:1, 1:Nyingi) zimeundwa;
  • Usajili huundwa kwa ajili ya mabadiliko katika sifa za kitu (INotifyPropertyChanged) na kwa kuongeza au kuondoa vipengele kwenye mkusanyiko (INotifyCollectionChanged);
  • Usajili unapoanzishwa, kitu kilichobadilishwa kinaongezwa kwenye foleni ya kuandika kwenye hifadhi ya data;
  • Mabadiliko kwenye hifadhi huhifadhiwa mara kwa mara (kwenye kipima muda) kwenye uzi wa nyuma;
  • Unapotoka kwenye programu, mabadiliko pia yanahifadhiwa kwenye hifadhi.

Mfano wa kanuni

Kuongeza utegemezi muhimu

// Основная библиотека
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

Tunaelezea mfano wa data ambao utahifadhiwa kwenye hifadhi

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

Kisha mfano wa kitu:

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

Na mwishowe, darasa la hazina lenyewe la kupata data:

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

Unda mfano wa ObjectRepository:

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

Ikiwa mradi utatumia HangFire

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

Kuingiza kitu kipya:

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

Kwa simu hii, kitu MzaziModel inaongezwa kwa kache ya ndani na foleni ya kuandika kwenye hifadhidata. Kwa hiyo, operesheni hii inachukua O (1), na kitu hiki kinaweza kufanyiwa kazi mara moja.

Kwa mfano, kupata kitu hiki kwenye hazina na kuthibitisha kuwa kitu kilichorejeshwa ni mfano sawa:

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

Nini kinaendelea? Weka () anarudi JedwaliKamusi, ambayo ina SanjariKamusi na hutoa utendakazi wa ziada wa faharasa za msingi na za upili. Hii hukuruhusu kuwa na njia za kutafuta kwa Id (au faharisi zingine za kiholela za watumiaji) bila kurudia tena juu ya vitu vyote.

Wakati wa kuongeza vitu ObjectRepository usajili huongezwa ili kubadilisha sifa zao, kwa hivyo mabadiliko yoyote katika sifa pia husababisha kitu hiki kuongezwa kwenye foleni ya uandishi. 
Kusasisha mali kutoka nje inaonekana sawa na kufanya kazi na kitu cha POCO:

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

Unaweza kufuta kitu kwa njia zifuatazo:

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

Hii pia huongeza kipengee kwenye foleni ya kufuta.

Je, kuokoa hufanya kazi vipi?

ObjectRepository wakati vitu vinavyofuatiliwa vinabadilika (ama kuongeza au kufuta, au kubadilisha sifa), huinua tukio ModelImebadilishwaumejisajili kwa Uhifadhi. Utekelezaji Uhifadhi tukio linapotokea ModelImebadilishwa mabadiliko yanawekwa kwenye foleni 3 - kwa kuongeza, kusasisha na kufuta.

Pia utekelezaji Uhifadhi baada ya kuanzishwa, huunda kipima muda ambacho husababisha mabadiliko kuhifadhiwa kila baada ya sekunde 5. 

Kwa kuongezea, kuna API ya kulazimisha simu ya kuokoa: ObjectRepository.Hifadhi().

Kabla ya kila kuokoa, shughuli zisizo na maana huondolewa kwanza kwenye foleni (kwa mfano, matukio ya duplicate - wakati kitu kilibadilishwa mara mbili au haraka aliongeza / kuondolewa vitu), na kisha tu kuokoa yenyewe. 

Katika hali zote, kitu kizima cha sasa kinahifadhiwa, kwa hiyo inawezekana kwamba vitu vinahifadhiwa kwa utaratibu tofauti kuliko walivyobadilishwa, ikiwa ni pamoja na matoleo mapya zaidi ya vitu kuliko wakati wa kuongezwa kwenye foleni.

Kuna nini tena?

  • Maktaba zote zinatokana na NET Standard 2.0. Inaweza kutumika katika mradi wowote wa kisasa wa NET.
  • API ni salama kwa uzi. Makusanyo ya ndani yanatekelezwa kwa kuzingatia SanjariKamusi, washughulikiaji wa hafla wana kufuli au hawazihitaji. 
    Kitu pekee kinachostahili kukumbuka ni kupiga simu ObjectRepository.Hifadhi();
  • Faharasa za kiholela (zinahitaji upekee):

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

Nani anaitumia?

Binafsi, nilianza kutumia mbinu hii katika miradi yote ya hobby kwa sababu ni rahisi na hauhitaji gharama kubwa kwa kuandika safu ya upatikanaji wa data au kupeleka miundombinu nzito. Binafsi, kuhifadhi data katika litedb au faili kawaida hunitosha. 

Lakini siku za nyuma, wakati uanzishaji wa sasa wa EscapeTeams (Nilidhani hapa ni, pesa - lakini hapana, uzoefu tena) - kutumika kuhifadhi data katika Hifadhi ya Jedwali la Azure.

Mipango ya siku zijazo

Ningependa kurekebisha moja ya hasara kuu za mbinu hii - kuongeza usawa. Ili kufanya hivyo, unahitaji shughuli zilizosambazwa (sic!), au ufanye uamuzi wa dhamira kwamba data sawa kutoka kwa matukio tofauti haipaswi kubadilika, au waache wabadilike kulingana na kanuni "nani wa mwisho ni sahihi."

Kwa mtazamo wa kiufundi, naona mpango ufuatao iwezekanavyo:

  • Hifadhi EventLog na Snapshot badala ya modeli ya kitu
  • Tafuta matukio mengine (ongeza ncha za matukio yote kwenye mipangilio? ugunduzi wa udp? bwana/mtumwa?)
  • Rudia kati ya matukio ya EventLog kupitia algoriti yoyote ya makubaliano, kama vile RAFT.

Pia kuna shida nyingine ambayo inanitia wasiwasi - kufutwa kwa kasino, au kugundua kesi za kufutwa kwa vitu ambavyo vina viungo kutoka kwa vitu vingine. 

Msimbo wa chanzo

Ikiwa umesoma hadi hapa, basi kilichobaki ni kusoma nambari; inaweza kupatikana kwenye GitHub:
https://github.com/DiverOfDark/ObjectRepository

Chanzo: mapenzi.com

Kuongeza maoni