{"id":33839,"date":"2019-10-31T21:54:56","date_gmt":"2019-10-31T18:54:56","guid":{"rendered":"https:\/\/prohoster.info\/blog\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov\/"},"modified":"2019-10-31T21:54:56","modified_gmt":"2019-10-31T18:54:56","slug":"objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","status":"publish","type":"post","link":"https:\/\/prohoster.info\/nl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 .NET in-memory repository-patroon voor uw thuisprojecten","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Waarom alle gegevens in het geheugen opslaan?<\/h2>\n<p><\/p>\n<p>Voor het opslaan van gegevens van een website of backend zal de eerste keuze van de meeste verstandige mensen een SQL-database zijn.\u00a0<\/p>\n<p><\/p>\n<p>Maar soms komt de gedachte op dat het datamodel niet geschikt is voor SQL: bijvoorbeeld, bij het opbouwen van zoekfunctionaliteit of een sociaal netwerk zijn complexe relaties tussen objecten vereist.\u00a0<\/p>\n<p><\/p>\n<p><strong>Het is het slechtste als je in een team werkt en een collega kan geen snelle queries opstellen. Hoeveel tijd heb je besteed aan het oplossen van N+1 problemen en het cre\u00ebren van extra indexen, zodat de SELECT op de startpagina binnen een redelijke tijd werkt?<\/strong><\/p>\n<p><\/p>\n<p>Een andere populaire benadering is NoSQL. Enkele jaren geleden was er veel hype rond dit onderwerp \u2014 bij elke gelegenheid werd MongoDB uitgerold en was men blij met de antwoorden in de vorm van JSON-documenten. <em>(Overigens, hoeveel tijdelijke oplossingen moesten er worden bedacht vanwege cyclische referenties in documenten?)<\/em>.<\/p>\n<p><\/p>\n<p>Ik stel voor om nog een alternatieve manier te proberen \u2014 waarom niet proberen om alle gegevens in het geheugen van de applicatie op te slaan, en deze periodiek in een willekeurige opslag (bestand, externe database) op te slaan?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Geheugen is goedkoop geworden, en de meeste mogelijke gegevens van kleine en middelgrote projecten passen in 1 GB geheugen. <em>(Bijvoorbeeld, mijn favoriete persoonlijke project \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">financi\u00eble tracker<\/a><\/noindex>, die dagelijkse statistieken en de geschiedenis van mijn uitgaven, saldi en transacties over anderhalf jaar bijhoudt, verbruikt slechts 45 MB geheugen.)<\/em><\/p>\n<p><\/p>\n<p>Voordelen:<\/p>\n<p><\/p>\n<ul>\n<li>Toegang tot gegevens wordt eenvoudiger \u2014 je hoeft je geen zorgen te maken over queries, lazy loading, de bijzonderheden van ORM, het werk is met gewone C# objecten;<\/li>\n<li>Er zijn geen problemen met toegang vanuit verschillende threads;<\/li>\n<li>Heel snel \u2014 geen netwerkverzoeken, geen codevertaling naar een querytaal, geen (de)serialisatie van objecten nodig;<\/li>\n<li>Het is toegestaan om gegevens in elk formaat op te slaan \u2014 zelfs in XML op schijf, of in SQL Server, of in Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Nadelen:<\/p>\n<p><\/p>\n<ul>\n<li>Horizontale schaalbaarheid gaat verloren, en als gevolg daarvan kan je geen zero downtime deployments maken;<\/li>\n<li>Als de applicatie crasht, kan er een deel van de gegevens verloren gaan. <em>(Maar onze applicatie crasht toch nooit, toch?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">How does it work?<\/h2>\n<p><\/p>\n<p>Het algoritme is als volgt:<\/p>\n<p><\/p>\n<ul>\n<li>Bij de start wordt er verbinding gemaakt met de gegevensopslag en worden de gegevens geladen;<\/li>\n<li>Er wordt een objectmodel opgebouwd, primaire indexen, en relationele indexen (1:1, 1:Many);<\/li>\n<li>Een abonnement wordt aangemaakt voor wijzigingen in objecteigenschappen (INotifyPropertyChanged) en voor het toevoegen of verwijderen van elementen in de collectie (INotifyCollectionChanged);<\/li>\n<li>Wanneer het abonnement wordt geactiveerd, wordt het gewijzigde object aan de wachtrij toegevoegd voor opslaan in de gegevensopslag;<\/li>\n<li>Periodiek (via een timer) worden in de achtergrond veranderingen opgeslagen in de opslag;<\/li>\n<li>Bij het afsluiten van de applicatie worden de wijzigingen ook in de opslag opgeslagen.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Voorbeeldcode<\/h2>\n<p>\n<b class=\"spoiler_title\">Voeg de benodigde afhankelijkheden toe<\/b><\/p>\n<pre><code class=\"cs\">\/\/ \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\nInstall-Package OutCode.EscapeTeams.ObjectRepository\n\u00a0 \u00a0\u00a0\n\/\/ \u0425\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\n\/\/ \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0442\u043e, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.\nInstall-Package OutCode.EscapeTeams.ObjectRepository.File\nInstall-Package OutCode.EscapeTeams.ObjectRepository.LiteDb\nInstall-Package OutCode.EscapeTeams.ObjectRepository.AzureTableStorage\n\u00a0 \u00a0\u00a0\n\/\/ \u041e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e - \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f Hangfire\n\/\/ Install-Package OutCode.EscapeTeams.ObjectRepository.Hangfire<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Beschrijf het gegevensmodel dat in de opslag zal worden bewaard<\/b><\/p>\n<pre><code class=\"cs\">public class ParentEntity : BaseEntity\n{\n\u00a0 \u00a0 public ParentEntity(Guid id) =&gt; Id = id;\n}\n\u00a0 \u00a0 \npublic class ChildEntity : BaseEntity\n{\n\u00a0 \u00a0 public ChildEntity(Guid id) =&gt; Id = id;\n\u00a0 \u00a0 public Guid ParentId { get; set; }\n\u00a0 \u00a0 public string Value { get; set; }\n}<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Dan het objectmodel:<\/b><\/p>\n<pre><code class=\"cs\">public class ParentModel : ModelBase\n{\n\u00a0 \u00a0 public ParentModel(ParentEntity entity)\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 Entity = entity;\n\u00a0 \u00a0 }\n\u00a0 \u00a0 \n\u00a0 \u00a0 public ParentModel()\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 Entity = new ParentEntity(Guid.NewGuid());\n\u00a0 \u00a0 }\n\u00a0 \u00a0 \n\u00a0 \u00a0 public Guid? NullableId =&gt; null;\n\u00a0 \u00a0 \n\u00a0 \u00a0 \/\/ Voorbeeld van 1:Many relatie\n\u00a0 \u00a0 public IEnumerable Children =&gt; Multiple(x =&gt; x.ParentId);\n\u00a0 \u00a0 \n\u00a0 \u00a0 protected override BaseEntity Entity { get; }\n}\n\u00a0 \u00a0 \npublic class ChildModel : ModelBase\n{\n\u00a0 \u00a0 private ChildEntity _childEntity;\n\u00a0 \u00a0 \n\u00a0 \u00a0 public ChildModel(ChildEntity entity)\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 _childEntity = entity;\n\u00a0 \u00a0 }\n\u00a0 \u00a0 \n\u00a0 \u00a0 public ChildModel()\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 _childEntity = new ChildEntity(Guid.NewGuid());\n\u00a0 \u00a0 }\n\u00a0 \u00a0 \n\u00a0 \u00a0 public Guid ParentId\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 get =&gt; _childEntity.ParentId;\n\u00a0 \u00a0 \u00a0 \u00a0 set =&gt; UpdateProperty(() =&gt; _childEntity.ParentId, value);\n\u00a0 \u00a0 }\n\u00a0 \u00a0 \n\u00a0 \u00a0 public string Value\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 get =&gt; _childEntity.Value;\n\u00a0 \u00a0 \u00a0 \u00a0 set =&gt; UpdateProperty(() =&gt; _childEntity.Value, value\n\u00a0 \u00a0 }\n\u00a0 \u00a0 \n\u00a0 \u00a0 \/\/ Toegang met zoeken op index\n\u00a0 \u00a0 public ParentModel Parent =&gt; Single(ParentId);\n\u00a0 \u00a0 \n\u00a0 \u00a0 protected override BaseEntity Entity =&gt; _childEntity;\n}<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">En tenslotte de repository-klasse voor gegevens toegang:<\/b><\/p>\n<pre><code class=\"cs\">public class MyObjectRepository : ObjectRepositoryBase\n{\n\u00a0 \u00a0 public MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 IsReadOnly = true; \/\/ Voor tests, maakt het mogelijk om wijzigingen niet op te slaan in de database\n\u00a0 \u00a0 \n\u00a0 \u00a0 \u00a0 \u00a0 AddType((ParentEntity x) =&gt; new ParentModel(x));\n\u00a0 \u00a0 \u00a0 \u00a0 AddType((ChildEntity x) =&gt; new ChildModel(x));\n\u00a0 \u00a0 \n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ Als Hangfire wordt gebruikt en de gegevensmodel moet worden opgeslagen voor Hangfire in ObjectRepository\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ this.RegisterHangfireScheme();\n\u00a0 \u00a0 \n\u00a0 \u00a0 \u00a0 \u00a0 Initialize();\n\u00a0 \u00a0 }\n}<\/code><\/pre>\n<p><\/p>\n<p>Maak een instantie van ObjectRepository aan:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var memory = new MemoryStream();\nvar db = new LiteDatabase(memory);\nvar dbStorage = new LiteDbStorage(db);\n\u00a0 \u00a0 \nvar repository = new MyObjectRepository(dbStorage);\nawait repository.WaitForInitialize();<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Als HangFire in het project zal worden gebruikt<\/b><\/p>\n<pre><code class=\"cs\">public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)\n{\n\u00a0 \u00a0 services.AddHangfire(s =&gt; s.UseHangfireStorage(objectRepository));\n}<\/code><\/pre>\n<p><\/p>\n<p>Invoegen van een nieuw object:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Bij deze aanroep wordt het object <strong>ParentModel<\/strong> toegevoegd aan zowel de lokale cache als de wachtrij voor database-invoer. Daarom neemt deze bewerking O(1) in beslag, en kan met dit object onmiddellijk worden gewerkt.<\/p>\n<p><\/p>\n<p>Bijvoorbeeld, om dit object in de repository te vinden en te bevestigen dat het teruggekeerde object hetzelfde exemplaar is:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var parents = repository.Set();\nvar myParent = parents.Find(newParent.Id);\nAssert.IsTrue(ReferenceEquals(myParent, newParent));<\/code><\/pre>\n<p><\/p>\n<p>Wat gebeurt er hierbij? <em>Set()<\/em> geeft hij <em>TableDictionary<\/em>, dat bevat <em>ConcurrentDictionary<\/em> en biedt extra functionaliteit voor primaire en secundaire indexen. Dit stelt je in staat om methoden te hebben voor het zoeken op Id (of andere willekeurige gebruikersindexen) zonder een volledige doorzoeking van alle objecten.<\/p>\n<p><\/p>\n<p>Bij het toevoegen van objecten aan <em>ObjectRepository<\/em> wordt een abonnement op de wijziging van hun eigenschappen toegevoegd, dus elke wijziging van de eigenschappen leidt ook tot het toevoegen van dit object aan de wachtrij voor invoer.\u00a0<br \/>\nHet bijwerken van eigenschappen van buitenaf ziet er net zo uit als werken met een POCO-object:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Bijgewerkt waarde\";<\/code><\/pre>\n<p><\/p>\n<p>Een object kan op de volgende manieren worden verwijderd:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">repository.Remove(myParent);\nrepository.RemoveRange(otherParents);\nrepository.Remove(x =&gt; !x.Children.Any());<\/code><\/pre>\n<p><\/p>\n<p>Bij dit alles wordt ook het object aan de wachtrij voor verwijdering toegevoegd.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Hoe werkt opslaan?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> bij het wijzigen van de gevolgde objecten (zoals toevoegen of verwijderen, evenals het wijzigen van eigenschappen) wordt een gebeurtenis opgeroepen <em>ModelChanged<\/em>, waaraan is geabonneerd <em>IStorage<\/em>. Implementaties <em>IStorage<\/em> bij het optreden van de gebeurtenis <em>ModelChanged<\/em> stapelen wijzigingen in 3 wachtrijen \u2014 voor toevoegen, voor bijwerken en voor verwijderen.<\/p>\n<p><\/p>\n<p>Bovendien cre\u00ebren implementaties <em>IStorage<\/em> bij initiatie een timer die elke 5 seconden het opslaan van wijzigingen aanroept.\u00a0<\/p>\n<p><\/p>\n<p><em>Daarnaast bestaat er een API voor het geforceerd aanroepen van opslag: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Voor elke opslag worden eerst zinloze bewerkingen uit de wachtrijen verwijderd (bijvoorbeeld duplicaten van gebeurtenissen \u2014 wanneer een object twee keer is gewijzigd of snelle toevoegingen\/verwijderingen van objecten), en pas daarna vindt de eigenlijke opslag plaats.\u00a0<\/p>\n<p><\/p>\n<p><em>In alle gevallen wordt het actuele object in zijn geheel opgeslagen, dus het is mogelijk dat objecten in een andere volgorde worden opgeslagen dan ze zijn gewijzigd, en zelfs dat nieuwere versies van objecten kunnen worden opgeslagen dan op het moment van toevoeging aan de wachtrij.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Wat is er nog meer?<\/h2>\n<p><\/p>\n<ul>\n<li>Alle bibliotheken zijn gebaseerd op .NET Standard 2.0. Kan worden gebruikt in elk modern .NET-project.<\/li>\n<li>De API is thread-safe. Interne collecties zijn ge\u00efmplementeerd op basis van <em>ConcurrentDictionary<\/em>, event handlers hebben ofwel locking nodig of zijn er niet van afhankelijk.\u00a0<br \/>\nHet enige om te onthouden is dat je bij het afsluiten van de applicatie moet aanroepen <em>ObjectRepository.Save();<\/em><\/li>\n<li>Arbitraire indexen (vereisen uniciteit):<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"cs\">repository.Set&lt;ChildModel&gt;().AddIndex(x =&gt; x.Value);\nrepository.Set&lt;ChildModel&gt;().Find(x =&gt; x.Value, \"myValue\");<\/code><\/pre>\n<p><\/p>\n<h2 id=\"kto-eto-ispolzuet\">Wie gebruikt dit?<\/h2>\n<p><\/p>\n<p>Persoonlijk ben ik deze aanpak in al mijn hobbyprojecten gaan gebruiken, omdat deze handig is en geen grote kosten met zich meebrengt voor het schrijven van een datatoegangslayer of het opzetten van zware infrastructuur. Voor mij is het meestal voldoende om gegevens op te slaan in litedb of in een bestand.\u00a0<\/p>\n<p><\/p>\n<p>Maar in het verleden, toen mijn team het inmiddels ter ziele gegane startup EscapeTeams deed (<em>ik dacht hier is geld \u2014 maar nee, weer ervaring<\/em>) \u2014 gebruikten we Azure Table Storage voor het opslaan van gegevens.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Toekomstplannen<\/h2>\n<p><\/p>\n<p>We willen een van de belangrijkste nadelen van deze aanpak verhelpen: horizontale schaalbaarheid. Hiervoor zijn ofwel gedistribueerde transacties nodig (sic!), ofwel moet er een krachtig besluit worden genomen dat dezelfde gegevens uit verschillende instanties niet mogen veranderen, of ze mogen veranderen volgens het principe 'wie het laatst komt, heeft gelijk'.<\/p>\n<p><\/p>\n<p>Vanuit technisch oogpunt zie ik de volgende opzet mogelijk:<\/p>\n<p><\/p>\n<ul>\n<li>Bewaar in plaats van het objectmodel EventLog en Snapshot<\/li>\n<li>Ontdek andere instanties (alle eindpunten van alle instanties aan de instellingen toevoegen? UDP-ontdekking? master\/slave?)<\/li>\n<li>Repliceer EventLog tussen instanties via een van de consensusalgoritmen, zoals RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Er is ook nog een ander probleem dat me zorgen baart \u2014 dat is cascadeverwijdering of het detecteren van gevallen van verwijdering van objecten waarnaar wordt verwezen vanuit andere objecten.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Broncode<\/h2>\n<p><\/p>\n<p>Als je dit tot hier hebt gelezen \u2014 dan hoef je verder alleen nog de code te lezen, die je op GitHub kunt vinden:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>Bron: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/452232\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0417\u0430\u0447\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438? \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0430\u0439\u0442\u0430 \u0438\u043b\u0438 \u0431\u0435\u043a\u044d\u043d\u0434\u0430 \u043f\u0435\u0440\u0432\u044b\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0437\u0434\u0440\u0430\u0432\u043e\u043c\u044b\u0441\u043b\u044f\u0449\u0438\u0445 \u043b\u044e\u0434\u0435\u0439 \u0432\u044b\u0431\u0435\u0440\u0435\u0442 SQL \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0 \u041d\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u0432 \u0433\u043e\u043b\u043e\u0432\u0443 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043c\u044b\u0441\u043b\u044c \u0447\u0442\u043e \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f SQL: \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0438 \u043f\u043e\u0438\u0441\u043a\u0430 \u0438\u043b\u0438 \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0433\u0440\u0430\u0444\u0430 \u043d\u0443\u0436\u0435\u043d \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u043c \u0441\u0432\u044f\u0437\u044f\u043c \u043c\u0435\u0436\u0434\u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438.\u00a0 \u0425\u0443\u0436\u0435 \u0432\u0441\u0435\u0433\u043e \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f, \u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[702],"tags":[],"class_list":["post-33839","post","type-post","status-publish","format-standard","hentry","category-news"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0417\u0430\u0447\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438? \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0430\u0439\u0442\u0430 \u0438\u043b\u0438 \u0431\u0435\u043a\u044d\u043d\u0434\u0430 \u043f\u0435\u0440\u0432\u044b\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0437\u0434\u0440\u0430\u0432\u043e\u043c\u044b\u0441\u043b\u044f\u0449\u0438\u0445 \u043b\u044e\u0434\u0435\u0439 \u0432\u044b\u0431\u0435\u0440\u0435\u0442 SQL \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/nl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"nl_NL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47ObjectRepository \u2014 .NET in-memory repository pattern \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u0434\u043e\u043c\u0430\u0448\u043d\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0417\u0430\u0447\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438? \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0430\u0439\u0442\u0430 \u0438\u043b\u0438 \u0431\u0435\u043a\u044d\u043d\u0434\u0430 \u043f\u0435\u0440\u0432\u044b\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0437\u0434\u0440\u0430\u0432\u043e\u043c\u044b\u0441\u043b\u044f\u0449\u0438\u0445 \u043b\u044e\u0434\u0435\u0439 \u0432\u044b\u0431\u0435\u0440\u0435\u0442 SQL \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/nl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:54:56+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:54:56+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47ObjectRepository \u2014 .NET in-memory repository pattern voor je thuisprojecten | ProHoster","description":"Waarom alle gegevens in het geheugen opslaan? Voor het opslaan van gegevens van een website of backend kiest de meerderheid van de verstandige mensen als eerste voor een SQL-database.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47ObjectRepository \u2014 .NET in-memory repository pattern \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u0434\u043e\u043c\u0430\u0448\u043d\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 | ProHoster","og:description":"\u0417\u0430\u0447\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438? \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0430\u0439\u0442\u0430 \u0438\u043b\u0438 \u0431\u0435\u043a\u044d\u043d\u0434\u0430 \u043f\u0435\u0440\u0432\u044b\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0437\u0434\u0440\u0430\u0432\u043e\u043c\u044b\u0441\u043b\u044f\u0449\u0438\u0445 \u043b\u044e\u0434\u0435\u0439 \u0432\u044b\u0431\u0435\u0440\u0435\u0442 SQL \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.","og:url":"https:\/\/prohoster.info\/nl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:54:56+00:00","article:modified_time":"2019-10-31T18:54:56+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"33839","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 16:56:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:32:27","updated":"2026-01-21 16:56:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}