{"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\/no\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 .NET in-memory repositoriesmodell for dine hjemmeprosjekter","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Hvorfor lagre all data i minnet?<\/h2>\n<p><\/p>\n<p>For lagring av nettsteddata eller backend, er det f\u00f8rste \u00f8nsket til de fleste fornuftige personer \u00e5 velge en SQL-database.\u00a0<\/p>\n<p><\/p>\n<p>Men noen ganger sl\u00e5r tanken ned at datamodellen ikke passer for SQL: for eksempel, n\u00e5r man bygger s\u00f8k eller en sosial graf, er det behov for s\u00f8k p\u00e5 komplekse relasjoner mellom objekter.\u00a0<\/p>\n<p><\/p>\n<p><strong>Det verste er situasjonen n\u00e5r man jobber i et team, og en kollega ikke kan lage effektive sp\u00f8rringer. Hvor mye tid har du brukt p\u00e5 \u00e5 l\u00f8se N+1-problemer og p\u00e5 \u00e5 lage ekstra indekser, s\u00e5 SELECT p\u00e5 hovedsiden kan kj\u00f8res p\u00e5 en rimelig tid?<\/strong><\/p>\n<p><\/p>\n<p>En annen popul\u00e6r tiln\u00e6rming er NoSQL. For flere \u00e5r siden var det stor hype rundt dette temaet \u2013 for enhver passende anledning ble MongoDB satt opp, og man gledet seg over svar i form av JSON-dokumenter. <em>(Forresten, hvor mange l\u00f8sninger m\u00e5tte man lage p\u00e5 grunn av sirkul\u00e6re referanser i dokumentene?)<\/em>.<\/p>\n<p><\/p>\n<p>Jeg foresl\u00e5r \u00e5 pr\u00f8ve en annen, alternativ metode \u2013 hvorfor ikke pr\u00f8ve \u00e5 lagre all data i applikasjonens minne, periodisk lagre i en vilk\u00e5rlig lagring (fil, ekstern database)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Minne har blitt billig, og alle potensielle data for flesteparten av sm\u00e5 og mellomstore prosjekter vil passe inn i 1 GB minne. <em>(For eksempel, mitt favoritt hjemmeprosjekt \u2013 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">en \u00f8konomisk tracker<\/a><\/noindex>, som f\u00f8rer daglig statistikk og historikk over mine utgifter, saldi og transaksjoner over halvannet \u00e5r, bruker bare 45 MB minne.)<\/em><\/p>\n<p><\/p>\n<p>Fordeler:<\/p>\n<p><\/p>\n<ul>\n<li>Tilgang til data blir enklere \u2013 man m\u00e5 ikke bekymre seg for sp\u00f8rringer, lat lasting, spesifikasjoner av ORM; arbeidet skjer med vanlige C# objekter;<\/li>\n<li>Ingen problemer relatert til tilgang fra ulike tr\u00e5der;<\/li>\n<li>Veldig raskt \u2013 ingen nettverksforesp\u00f8rsel, ingen oversettelse av kode til sp\u00f8rringsspr\u00e5k, ingen (de)serialisering av objekter;<\/li>\n<li>Det er akseptabelt \u00e5 lagre data i hvilken som helst form \u2013 enten i XML p\u00e5 disk, i SQL Server, eller i Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ulemper:<\/p>\n<p><\/p>\n<ul>\n<li>Man mister horisontal skalering, og som en konsekvens kan man ikke gj\u00f8re null nedetid distribusjon;<\/li>\n<li>Hvis applikasjonen krasjer, kan man delvis miste data. <em>(Men, applikasjonen v\u00e5r krasjer vel aldri, sant?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Hvordan fungerer det?<\/h2>\n<p><\/p>\n<p>Algoritmen er som f\u00f8lger:<\/p>\n<p><\/p>\n<ul>\n<li>Ved oppstart opprettes det en forbindelse til datalagring, og data lastes inn;<\/li>\n<li>Objektmodellen, prim\u00e6re indekser, og relasjonsindekser (1:1, 1:mange) bygges.<\/li>\n<li>En abonnemang p\u00e5 \u00e4ndringar av objektens egenskaper (INotifyPropertyChanged) och p\u00e5 till\u00e4gg eller borttagning av element i samlingen (INotifyCollectionChanged) skapas;<\/li>\n<li>Vid abonnemangets utl\u00f6sning l\u00e4ggs det \u00e4ndrade objektet i k\u00f6 f\u00f6r att skrivas till datalagringen;<\/li>\n<li>\u00c4ndringar sparas periodiskt (via timer) i bakgrundstr\u00e5den till lagringssystemet;<\/li>\n<li>\u00c4ndringar sparas ocks\u00e5 till lagringssystemet vid utloggning fr\u00e5n applikationen.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Kodeeksempel<\/h2>\n<p>\n<b class=\"spoiler_title\">L\u00e4gg till n\u00f6dv\u00e4ndiga beroenden<\/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\">Beskriv datamodellen som ska sparas i lagringen<\/b><\/p>\n<pre><code class=\"cs\">public class ParentEntity : BaseEntity\n{\n    public ParentEntity(Guid id) =&gt; Id = id;\n}\n    \npublic class ChildEntity : BaseEntity\n{\n    public ChildEntity(Guid id) =&gt; Id = id;\n    public Guid ParentId { get; set; }\n    public string Value { get; set; }\n}<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Sedan objektmodellen:<\/b><\/p>\n<pre><code class=\"cs\">public class ParentModel : ModelBase\n{\n    public ParentModel(ParentEntity entity)\n    {\n        Entity = entity;\n    }\n    \n    public ParentModel()\n    {\n        Entity = new ParentEntity(Guid.NewGuid());\n    }\n    \n    public Guid? NullableId =&gt; null;\n    \n    \/\/ Exempel p\u00e5 relation 1: M\u00e5nga\n    public IEnumerable Children =&gt; Multiple(x =&gt; x.ParentId);\n    \n    protected override BaseEntity Entity { get; }\n}\n    \npublic class ChildModel : ModelBase\n{\n    private ChildEntity _childEntity;\n    \n    public ChildModel(ChildEntity entity)\n    {\n        _childEntity = entity;\n    }\n    \n    public ChildModel() \n    {\n        _childEntity = new ChildEntity(Guid.NewGuid());\n    }\n    \n    public Guid ParentId\n    {\n        get =&gt; _childEntity.ParentId;\n        set =&gt; UpdateProperty(() =&gt; _childEntity.ParentId, value);\n    }\n    \n    public string Value\n    {\n        get =&gt; _childEntity.Value;\n        set =&gt; UpdateProperty(() =&gt; _childEntity.Value, value\n    }\n    \n    \/\/ \u00c5tkomst med s\u00f6kning enligt index\n    public ParentModel Parent =&gt; Single(ParentId);\n    \n    protected override BaseEntity Entity =&gt; _childEntity;\n}<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Och slutligen sj\u00e4lva klass-repositoriet f\u00f6r \u00e5tkomst till data:<\/b><\/p>\n<pre><code class=\"cs\">public class MyObjectRepository : ObjectRepositoryBase\n{\n    public MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)\n    {\n        IsReadOnly = true; \/\/ F\u00f6r tester, till\u00e5ter att inte spara \u00e4ndringar i databasen\n    \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n    \n        \/\/ Om Hangfire anv\u00e4nds och databasen beh\u00f6ver lagra datamodellen f\u00f6r Hangfire i ObjectRepository\n        \/\/ this.RegisterHangfireScheme();\n    \n        Initialize();\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Skapa en instans av ObjectRepository:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var memory = new MemoryStream();\nvar db = new LiteDatabase(memory);\nvar dbStorage = new LiteDbStorage(db);\n    \nvar repository = new MyObjectRepository(dbStorage);\nawait repository.WaitForInitialize();<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Om projektet ska anv\u00e4nda HangFire<\/b><\/p>\n<pre><code class=\"cs\">public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)\n{\n    services.AddHangfire(s =&gt; s.UseHangfireStorage(objectRepository));\n}<\/code><\/pre>\n<p><\/p>\n<p>Infogning av ett nytt objekt:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Vid denna anrop l\u00e4ggs objektet <strong>ParentModel<\/strong> blir lagt til b\u00e5de i lokal hurtigminne og i k\u00f8en for skriving til databasen. Derfor tar denne operasjonen O(1), og dette objektet kan straks brukes.<\/p>\n<p><\/p>\n<p>For eksempel, for \u00e5 finne dette objektet i depotet og sikre at det returnerte objektet er den samme instansen:<\/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>Hva skjer her? <em>Set()<\/em> returnerer <em>TableDictionary<\/em>, som inneholder <em>ConcurrentDictionary<\/em> og gir ekstra funksjonalitet for prim\u00e6re og sekund\u00e6re indekser. Dette gj\u00f8r at det finnes metoder for \u00e5 s\u00f8ke etter Id (eller andre vilk\u00e5rlige brukerdefinerte indekser) uten \u00e5 m\u00e5tte iterere gjennom alle objektene.<\/p>\n<p><\/p>\n<p>N\u00e5r objekter legges til i <em>ObjectRepository<\/em> legges det ogs\u00e5 til et abonnement p\u00e5 endringer i deres egenskaper, s\u00e5 enhver endring av egenskapene f\u00f8rer ogs\u00e5 til at dette objektet legges i k\u00f8en for skriving.\u00a0<br \/>\nOppdatering av egenskaper utenfra ser lik ut som \u00e5 jobbe med et POCO-objekt:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Oppdatert verdi\";<\/code><\/pre>\n<p><\/p>\n<p>Objektet kan fjernes p\u00e5 f\u00f8lgende m\u00e5ter:<\/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>Her legges ogs\u00e5 objektet til i k\u00f8en for fjerning.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Hvordan fungerer lagringen?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> N\u00e5r det skjer endringer p\u00e5 de sporene objektene (som tillegg eller fjerning, samt endring av egenskaper) utl\u00f8ses en hendelse <em>ModelChanged<\/em>, som er abonnert p\u00e5 av <em>IStorage<\/em>. Implementeringene <em>IStorage<\/em> ved hendelsen <em>ModelChanged<\/em> samler endringene i 3 k\u00f8er \u2013 for tillegg, for oppdateringer og for fjerning.<\/p>\n<p><\/p>\n<p>Implementeringene <em>IStorage<\/em> oppretter ogs\u00e5 en timer ved initialisering som hver 5. sekund kaller lagring av endringene.\u00a0<\/p>\n<p><\/p>\n<p><em>I tillegg finnes det et API for \u00e5 tvinge frem lagring: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>F\u00f8r hver lagring skjer det f\u00f8rst en fjerning av meningsl\u00f8se operasjoner fra k\u00f8ene (for eksempel duplikater av hendelser \u2013 n\u00e5r objektet har blitt endret to ganger, eller rask tillegg\/fjerning av objekter), og deretter selve lagringen.\u00a0<\/p>\n<p><\/p>\n<p><em>I alle tilfeller lagres den aktuelle objektet i sin helhet, s\u00e5 det kan oppst\u00e5 situasjoner der objekter lagres i en annen rekkef\u00f8lge enn de endret seg, inkludert at nyere versjoner av objekter kan lagres enn p\u00e5 tidspunktet for legging i k\u00f8.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Hva mer er det?<\/h2>\n<p><\/p>\n<ul>\n<li>Alle bibliotekene er basert p\u00e5 .NET Standard 2.0. Kan brukes i ethvert moderne .NET-prosjekt.<\/li>\n<li>API-en er tr\u00e5d-sikker. Interne samlinger er implementert p\u00e5 basis av <em>ConcurrentDictionary<\/em>, hendelsesbehandlere har enten l\u00e5ser eller trenger dem ikke.\u00a0<br \/>\nDet eneste du b\u00f8r huske p\u00e5, er \u00e5 kalle <em>ObjectRepository.Save();<\/em><\/li>\n<li>Vilk\u00e5rlige indekser (krever unikhet):<\/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\">Hvem bruker dette?<\/h2>\n<p><\/p>\n<p>Personlig har jeg begynt \u00e5 bruke denne tiln\u00e6rmingen i alle hobbyprosjekter, fordi det er praktisk, og krever ikke store kostnader for \u00e5 skrive et datatilgangslag eller sette opp tung infrastruktur. For meg, er det vanligvis nok \u00e5 lagre data i litedb eller i en fil.\u00a0<\/p>\n<p><\/p>\n<p>Men tidligere, da teamet jobbet med den n\u00e5 nedlagte oppstarten EscapeTeams (<em>jeg tenkte, her er pengene \u2013 men nei, igjen er det erfaring<\/em>) \u2013 brukte vi Azure Table Storage for datalagring.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Fremtidsplaner<\/h2>\n<p><\/p>\n<p>Det er n\u00f8dvendig \u00e5 l\u00f8se en av de viktigste ulempene med denne tiln\u00e6rmingen \u2013 horisontal skalering. For dette er det n\u00f8dvendig med enten distribuerte transaksjoner (sic!), eller \u00e5 ta en avgj\u00f8rende beslutning om at de samme dataene fra forskjellige instanser ikke skal endres, eller la dem endres etter prinsippet om \u00abden siste er den som har rett\u00bb.<\/p>\n<p><\/p>\n<p>Fra et teknisk st\u00e5sted ser jeg en mulig l\u00f8sning:<\/p>\n<p><\/p>\n<ul>\n<li>Lagre i stedet for objektmodellen EventLog og Snapshot<\/li>\n<li>Finne andre instanser (legge til sluttpunktene for alle instanser i innstillingene? UDP oppdagelse? master\/slave?)<\/li>\n<li>Replisere mellom instanser EventLog via en hvilken som helst konsensusalgoritme, for eksempel RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Det finnes ogs\u00e5 et annet problem som bekymrer meg \u2013 dette med kaskade-sletting, eller \u00e5 oppdage tilfeller av sletting av objekter som det er lenker til fra andre objekter.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Kildekoden<\/h2>\n<p><\/p>\n<p>Hvis du har lest hit \u2013 er det bare \u00e5 lese koden videre, som du kan finne p\u00e5 GitHub:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>Kilde: <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.5 - 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\/no\/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.5\" \/>\n\t\t<meta property=\"og:locale\" content=\"nn_NO\" \/>\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\/no\/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 \u2013 .NET in-memory repository pattern for dine hjemmearbeidsprosjekter | ProHoster","description":"Hvorfor lagre alle data i minnet? For lagring av nettstedets eller backend-data vil de fleste fornuftige mennesker f\u00f8rst velge en SQL-database.","canonical_url":"https:\/\/prohoster.info\/no\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nn_NO","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\/no\/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\/no\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}