ObjectRepository - .NET in-memoria repositorium exemplum pro domo tua incepta

Cur omnia notitias in memoria conditis?

Ad situs reponendas seu notitias retrahendas, primum desiderium omnium sanissimorum hominum SQL datorum eligendum erit. 

Aliquando tamen in mentem venit quod exemplar notitiae SQL aptum non est: exempli gratia, cum investigationem vel grapham socialem construas, necesse est ut multiplices relationes inter obiecta quaeras. 

Pessima res est, cum in quadrigis et collega laboras, non scit queries vivos aedificare. Quantum temporis habuisti solvendas quaestiones N+1 et indices additos aedificas ut SELECTA in pagina principali rationabili temporis quantitate perficiat?

Alius popularis adventus est NoSQL. Ante aliquot annos circa hunc locum multum hype erat - quavis occasione opportunius MongoDB explicaverunt et laeti cum responsionibus in forma documentorum json (obiter quot fusum habuistis inserere propter nexus circulares in documentis?).

Moneo aliam quaerendo methodum alternativam - cur non omnia notitias in applicatione memoriae accommodare conaris, periodice salvam illam ad tabulas repositas (lima, remota database)? 

Memoria vilis facta est, et quaelibet possibilis notitia pro minimis et mediocribus inceptis in 1 GB memoriae apta erit. (Exempli gratia, mea ventus domus consilii est financial venatoquae cotidie statisticas et historiae meae expensas, stateras et transactiones per annum et dimidium servat, memoriae tantum 45 MB consumit.

pros,

  • Accessus ad notitias facilior fit - queries de pigris loadings, ORM notis non debes curare, cum objectis ordinariis C# laboras;
  • Nullae quaestiones coniunguntur cum accessu e filis diversis;
  • Celerrimus - nulla retis petitiones, nulla translationis codicis in linguam quaesitum, nulla obiciorum serializationis necessitas;
  • Placet notitias condere in quavis forma - fiat in XML in orbe, vel in SQL Servo, vel in Tabula Azure Repono.

cons:

  • Scala horizontalis amittitur, et consequenter instruere tempus nulla fieri non potest;
  • Si applicationis inruerit, data partim perdere potes. (Sed nostra applicatio nunquam inruerit, vox?)

Quid opus est?

Algorithmus talis est;

  • In initio, nexus cum notitia repono constituitur, et notitia oneratur;
  • Obiectum exemplar, indices primi et indices relativi (1:1, 1: multi) aedificantur;
  • Subscriptio creatur pro mutationibus proprietatum obiecti (INotifyPropertyChanged) et elementorum ad collectionem addendo vel tollendo (INotifyCollectionChanged);
  • Cum subscriptione utitur, obiectum mutatum additur queue scribendo ad tabularium notitiarum;
  • Mutationes repositionis intervenit (per timer) in filo curriculo servatae;
  • Cum applicatione exieris, mutationes etiam ad repono servatae sunt.

Exemplum Code

Addere necessarias clientelas

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

Describimus notitias exemplar quod in repono

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

Tum obiectum exemplar;

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

Ac denique ipsum genus repositorium pro notitia accessionis;

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

Create an ObjectRepository exempli gratia:

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

Si project utar HangFire

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

Inserens novum obiectum;

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

Cum hoc dico, obiectum ParentModel additur tum cache localis et queue pro datorum scripto. Ergo haec operatio est O, quae statim potest operari.

Exempli gratia, invenire hoc in reposito et verificare obiectum reversum idem esse instantia;

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

Quid accidit? Set () redit TableDictionaryquae continet ConcurrentDictionary ac additiciorum functionum indices primariae et secundariae. Hoc permittit tibi habere methodos quaerendi per Id (vel alios indices usorum arbitrarios) quin penitus iterando in omnia obiecta.

Cum addendo obiecti to ObjectRepository Subscriptio additur mutandis proprietatibus, ita etiam quaelibet mutatio in proprietatibus consequitur in hoc objecto addita scribenti queue. 
Adaequationis proprietates extrinsecus idem spectat ac laborat cum obiecto POCO;

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

Rem his modis delere potes:

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

Hoc etiam addit objectum deletionis queue.

Quomodo opus salutaris facit?

ObjectRepository cum monitored rerum mutationem (vel addendo vel delendo vel mutando proprietates) res movet ModelChangedsubscripsi to IStorage. Effectus IStorage si res est ModelChanged mutationes ponuntur in 3 queues - addendi, augendi, delendi.

Etiam implementations IStorage super initializationem, timentem efficiunt qui mutationes in singulis 5 secundis rebus salvis facit. 

Praeterea est API ad vim a nisi vocato; ObjectRepository.Save ().

Priusquam singulas res salvas, inanes operationes a queuis primum removentur (exempli gratia, eventus duplicatus - cum objectum bis vel cito additur/remotum mutatum est), et tunc demum ipsum salvare. 

In omnibus casibus, totum obiectum currente servatum est, ita fieri potest ut res alio ordine salvae sint quam mutatae sunt, etiam recentiores versiones obiectorum quam tempore queue additae sunt.

Quid aliud?

  • Omnes bibliothecae .NET Standard 2.0 innituntur. In aliquo moderno .NET adhiberi potest.
  • API tutum est filum. Internae collectiones perficiuntur secundum ConcurrentDictionarytracto eventum vel comas habent vel non egent. 
    Sola res memoria digna est vocatio ObjectRepository.Save();
  • Indices arbitrarii (requirere singularitatem);

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

Quis hoc utitur?

Personaliter, hanc accessionem in omnibus inceptis amabam uti coepi, quia opportunum est et magnas expensas non requirit ad accessum tabulatum scribendum vel ad graves infrastructuras explicandas. Personaliter, in litedb vel fasciculo notitias reponendas plerumque satis mihi est. 

Sed in praeterito, cum iam defuncti startup EscapeTeams (Putabam hic esse pecuniam - sed non, experiri iterum) - reponendarum notitiarum in tabula Azure attulere.

Consilia pro futuro

Praecipua incommoda huius accessionis - horizontalis scalis) figere velim. Ad hoc faciendum opus est vel transactiones distributae (sic!), vel vehemens ad arbitrium faciendum, ut eadem notitia ex diversis instantiis non mutetur, vel mutent secundum principium « qui ultimus est rectus est ».

Ex technica parte, hunc schema quam maxime video:

  • Copia EventLog et Snapshot loco objectum exemplar
  • Alias ​​instantias invenies (fines omnium instantiarum addendis ad uncinis? inventionis udp? domini/servi?)
  • Replicate inter Instantias EventLog per consensum quamlibet algorithmum, ut TRABS.

Est etiam alia quaestio quae me sollicitat - Cascades deletionem, seu detectio casuum deletionum objectorum, quae ex aliis objectis nexus habent. 

fons

Si huc usque perlegisti, omnia quae restant in codice legat, reperiri potest in GitHub;
https://github.com/DiverOfDark/ObjectRepository

Source: www.habr.com

Add a comment