{"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\/da\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 .NET in-memory repository pattern til dine hjemmeprojekter","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Hvorfor gemme alle data i hukommelsen?<\/h2>\n<p><\/p>\n<p>Til opbevaring af datanetv\u00e6rk eller backend vil de fleste fornuftige mennesker f\u00f8rst v\u00e6lge en SQL-database.\u00a0<\/p>\n<p><\/p>\n<p>Men nogle gange kommer man til at t\u00e6nke, at datamodellen ikke passer til SQL: for eksempel, n\u00e5r man bygger s\u00f8gning eller sociale netv\u00e6rk, er der behov for at s\u00f8ge i komplekse relationer mellem objekterne.\u00a0<\/p>\n<p><\/p>\n<p><strong>Det v\u00e6rste er, n\u00e5r man arbejder i et team, og en kollega ikke kan lave hurtige foresp\u00f8rgsler. Hvor meget tid har du brugt p\u00e5 at l\u00f8se N+1 problemer og p\u00e5 at bygge ekstra indekser, s\u00e5 SELECT p\u00e5 hovedsiden k\u00f8rte p\u00e5 et rimeligt tid?<\/strong><\/p>\n<p><\/p>\n<p>En anden popul\u00e6r tilgang er NoSQL. For flere \u00e5r siden var der stor hype omkring dette emne \u2014 for enhver passende situation satte man MongoDB op og gl\u00e6dede sig over svarene i form af JSON-dokumenter. <em>(Forresten, hvor mange 'workarounds' var n\u00f8dvendige p\u00e5 grund af cykliske referencer i dokumenterne?)<\/em>.<\/p>\n<p><\/p>\n<p>Jeg foresl\u00e5r at pr\u00f8ve en anden, alternativ metode \u2014 hvorfor ikke pr\u00f8ve at gemme alle data i applikationens hukommelse og periodisk gemme dem i en vilk\u00e5rlig lagringsenhed (fil, fjerndatabase)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Hukommelse er blevet billig, og alle potentielle data for de fleste sm\u00e5 og mellemstore projekter kan nemt v\u00e6re inden for 1 GB hukommelse. <em>(For eksempel, mit yndlingshjemmeprojekt \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">en finansiel tracker<\/a><\/noindex>, som holder daglig statistik og historik over mine udgifter, saldi og transaktioner i halvandet \u00e5r, bruger kun 45 MB hukommelse.)<\/em><\/p>\n<p><\/p>\n<p>Fordele:<\/p>\n<p><\/p>\n<ul>\n<li>Adgang til data bliver lettere \u2014 man skal ikke bekymre sig om foresp\u00f8rgsler, lazy loading, ORM-specifikke detaljer, arbejdet foreg\u00e5r med almindelige C# objekter;<\/li>\n<li>Ingen problemer relateret til adgang fra forskellige tr\u00e5de;<\/li>\n<li>Meget hurtigt \u2014 der er ingen netv\u00e6rksforesp\u00f8rgsler, ingen omkodning til foresp\u00f8rgselssprog, der er ikke behov for (de)serialisering af objekter;<\/li>\n<li>Det er acceptabelt at gemme data i enhver form \u2014 uanset om det er i XML p\u00e5 disken, 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>Horizontalt skalering g\u00e5r tabt, og som f\u00f8lge deraf kan man ikke lave zero downtime deployment;<\/li>\n<li>Hvis applikationen g\u00e5r ned \u2014 kan man delvist miste data. <em>(Men vores applikation g\u00e5r jo aldrig ned, eller hvad?)<\/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 opstart etableres en forbindelse til datalageret, og data indl\u00e6ses;<\/li>\n<li>En objektmodel, prim\u00e6re indekser og relationsindekser (1:1, 1:Many) opbygges;<\/li>\n<li>Der abonneres \u00e6ndringer i objektets egenskaber (INotifyPropertyChanged) samt tilf\u00f8jelse eller fjernelse af elementer fra samlingen (INotifyCollectionChanged);<\/li>\n<li>N\u00e5r abonnementet aktiveres, tilf\u00f8jes det \u00e6ndrede objekt til en k\u00f8 til opbevaring;<\/li>\n<li>Periodisk (via en timer) gemmes \u00e6ndringerne i baggrunden i opbevaringen;<\/li>\n<li>N\u00e5r applikationen lukkes, gemmes \u00e6ndringerne ogs\u00e5 i opbevaringen.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Code example<\/h2>\n<p>\n<b class=\"spoiler_title\">Tilf\u00f8jer n\u00f8dvendige afh\u00e6ngigheder<\/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\">Beskriver datamodellen, der skal gemmes i opbevaringen<\/b><\/p>\n<pre><code class=\"cs\">public class ParentEntity : BaseEntity\n{\n\tpublic ParentEntity(Guid id) =&gt; Id = id;\n}\n\t\npublic class ChildEntity : BaseEntity\n{\n\tpublic ChildEntity(Guid id) =&gt; Id = id;\n\tpublic Guid ParentId { get; set; }\n\tpublic string Value { get; set; }\n}<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Derefter objektmodellen:<\/b><\/p>\n<pre><code class=\"cs\">public class ParentModel : ModelBase\n{\n\tpublic ParentModel(ParentEntity entity)\n\t{\n\t\tEntity = entity;\n\t}\n\t\n\tpublic ParentModel()\n\t{\n\t\tEntity = new ParentEntity(Guid.NewGuid());\n\t}\n\t\n\tpublic Guid? NullableId =&gt; null;\n\t\n\t\/\/ Eksempel p\u00e5 1:Many forhold\n\tpublic IEnumerable Children =&gt; Multiple(x =&gt; x.ParentId);\n\t\n\tprotected override BaseEntity Entity { get; }\n}\n\t\npublic class ChildModel : ModelBase\n{\n\tprivate ChildEntity _childEntity;\n\t\n\tpublic ChildModel(ChildEntity entity)\n\t{\n\t\t_childEntity = entity;\n\t}\n\t\n\tpublic ChildModel() \n\t{\n\t\t_childEntity = new ChildEntity(Guid.NewGuid());\n\t}\n\t\n\tpublic Guid ParentId\n\t{\n\t\tget =&gt; _childEntity.ParentId;\n\t\tset =&gt; UpdateProperty(() =&gt; _childEntity.ParentId, value);\n\t}\n\t\n\tpublic string Value\n\t{\n\t\tget =&gt; _childEntity.Value;\n\t\tset =&gt; UpdateProperty(() =&gt; _childEntity.Value, value);\n\t}\n\t\n\t\/\/ Adgang med s\u00f8gning efter indeks\n\tpublic ParentModel Parent =&gt; Single(ParentId);\n\t\n\tprotected override BaseEntity Entity =&gt; _childEntity;\n}<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Og endelig selve klasse-repositoriet til dataadgang:<\/b><\/p>\n<pre><code class=\"cs\">public class MyObjectRepository : ObjectRepositoryBase\n{\n\tpublic MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)\n\t{\n\t\tIsReadOnly = true; \/\/ Til test, tillader ikke, at \u00e6ndringer gemmes i databasen\n\t\t\n\t\tAddType((ParentEntity x) =&gt; new ParentModel(x));\n\t\tAddType((ChildEntity x) =&gt; new ChildModel(x));\n\t\t\n\t\t\/\/ Hvis Hangfire bruges, og det er n\u00f8dvendigt at gemme datamodellen til Hangfire i ObjectRepository\n\t\t\/\/ this.RegisterHangfireScheme();\n\t\t\n\t\tInitialize();\n\t}\n}<\/code><\/pre>\n<p><\/p>\n<p>Opretter en instans af 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\t\nvar repository = new MyObjectRepository(dbStorage);\nawait repository.WaitForInitialize();<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">Hvis projektet vil bruge HangFire<\/b><\/p>\n<pre><code class=\"cs\">public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)\n{\n\tservices.AddHangfire(s =&gt; s.UseHangfireStorage(objectRepository));\n}<\/code><\/pre>\n<p><\/p>\n<p>Inds\u00e6ttelse af et nyt objekt:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Ved dette kald vil objektet <strong>ParentModel<\/strong> Tilf\u00f8jes b\u00e5de til den lokale cache og til k\u00f8en til databaseopdatering. Derfor tager denne operation O(1), og man kan straks arbejde med dette objekt.<\/p>\n<p><\/p>\n<p>For eksempel, for at finde dette objekt i depotet og sikre, at det returnerede objekt er den samme instans:<\/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>Hvad sker der her? <em>Set()<\/em> returnerer <em>TableDictionary<\/em>, som indeholder <em>ConcurrentDictionary<\/em> og tilbyder yderligere funktionalitet til prim\u00e6re og sekund\u00e6re indekser. Dette g\u00f8r det muligt at have metoder til s\u00f8gning efter Id (eller andre vilk\u00e5rlige brugerdefinerede indekser) uden at skulle gennemg\u00e5 alle objekterne.<\/p>\n<p><\/p>\n<p>N\u00e5r objekter tilf\u00f8jes til <em>ObjectRepository<\/em> , addes der en abonnement p\u00e5 \u00e6ndring af deres egenskaber, s\u00e5 enhver \u00e6ndring i egenskaberne medf\u00f8rer, at dette objekt ogs\u00e5 tilf\u00f8jes til k\u00f8en til databaseopdatering.\u00a0<br \/>\nOpdatering af egenskaber udefra ser ud p\u00e5 samme m\u00e5de som at arbejde med et POCO-objekt:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Opdateret v\u00e6rdi\";<\/code><\/pre>\n<p><\/p>\n<p>Et objekt kan fjernes p\u00e5 f\u00f8lgende m\u00e5der:<\/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>Dette medf\u00f8rer ogs\u00e5, at objektet tilf\u00f8jes til k\u00f8en til fjernelse.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Hvordan fungerer gemmes?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> N\u00e5r der sker \u00e6ndringer i de overv\u00e5gede objekter (b\u00e5de tilf\u00f8jelse og fjernelse samt \u00e6ndring af egenskaber), udl\u00f8ses en begivenhed <em>ModelChanged<\/em>, som er tilmeldt <em>IStorage<\/em>. Implementeringer <em>IStorage<\/em> ved opst\u00e5en af begivenheden <em>ModelChanged<\/em> samler \u00e6ndringerne i 3 k\u00f8er \u2014 tilf\u00f8jelse, opdatering og fjernelse.<\/p>\n<p><\/p>\n<p>Implementeringer <em>IStorage<\/em> opretter ogs\u00e5 en timer ved initialisering, som hver 5. sekund kalder til gemme \u00e6ndringer.\u00a0<\/p>\n<p><\/p>\n<p><em>Derudover findes der et API til at tvinge gemmeoperationer: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>F\u00f8r hver gemmes operation fjernes f\u00f8rst meningsl\u00f8se operationer fra k\u00f8erne (f.eks. dubletter af begivenheder \u2014 n\u00e5r et objekt er \u00e6ndret to gange eller hurtig tilf\u00f8jelse\/fjernelse af objekter), og kun derefter finder selve gemmen sted.\u00a0<\/p>\n<p><\/p>\n<p><em>I alle tilf\u00e6lde gemmes den aktuelle objekt fuldst\u00e6ndigt, s\u00e5 der kan opst\u00e5 situationer, hvor objekterne gemmes i en anden r\u00e6kkef\u00f8lge, end de blev \u00e6ndret, herunder at nyere versioner af objekter kan gemmes end da de blev tilf\u00f8jet til k\u00f8en.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Hvad er der ellers?<\/h2>\n<p><\/p>\n<ul>\n<li>Alle biblioteker er baseret p\u00e5 .NET Standard 2.0. Kan bruges i ethvert moderne .NET projekt.<\/li>\n<li>API er tr\u00e5dsikkert. Interne samlinger er implementeret p\u00e5 basis af <em>ConcurrentDictionary<\/em>, begivenhedsh\u00e5ndterere har enten l\u00e5s eller har ikke brug for dem.\u00a0<br \/>\nDet eneste, du skal huske p\u00e5, er at kalde <em>ObjectRepository.Save();<\/em><\/li>\n<li>Tilf\u00e6ldige indekser (kr\u00e6ver unikke v\u00e6rdier):<\/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 bruger dette?<\/h2>\n<p><\/p>\n<p>Personligt begyndte jeg at bruge denne tilgang i alle mine hobbyprojekter, fordi det er praktisk og ikke kr\u00e6ver store omkostninger til at skrive et datatilgangslag eller implementere tung infrastruktur. For mig er det som regel tilstr\u00e6kkeligt at gemme data i litedb eller i en fil.\u00a0<\/p>\n<p><\/p>\n<p>Men tidligere, da vi med teamet lavede den nu nedlagte opstart EscapeTeams (<em>t\u00e6nkte jeg, her er pengene \u2014 men nej, mere erfaring<\/em>) \u2014 brugte vi Azure Table Storage til datalagring.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Fremtidige planer<\/h2>\n<p><\/p>\n<p>Det er n\u00f8dvendigt at l\u00f8se en af de grundl\u00e6ggende ulemper ved denne tilgang \u2014 horisontal skalering. Dette kr\u00e6ver enten distribuerede transaktioner (sic!), eller at man tr\u00e6ffer en afg\u00f8rende beslutning om, at de samme data fra forskellige instanser ikke m\u00e5 \u00e6ndres, eller at de \u00e6ndres efter princippet \"den, der er sidst, har ret.\"<\/p>\n<p><\/p>\n<p>Fra et teknisk synspunkt ser jeg en mulig skema:<\/p>\n<p><\/p>\n<ul>\n<li>Gemme i stedet for en objektmodel EventLog og Snapshot<\/li>\n<li>Finder andre instanser (tilf\u00f8je endpoints for alle instanser i indstillingerne? udp discovery? master\/slave?)<\/li>\n<li>Replikere mellem instanser EventLog gennem en hvilken som helst af konsensusalgoritmerne, fx RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Derudover er der et andet problem, der bekymrer mig \u2014 det er kaskadedeletion eller opdagelse af tilf\u00e6lde af sletning af objekter, som der henvises til fra andre objekter.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Kildekode<\/h2>\n<p><\/p>\n<p>Hvis du har l\u00e6st s\u00e5 langt \u2014 er det kun tilbage at l\u00e6se koden, som du kan finde 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\/da\/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=\"da_DK\" \/>\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\/da\/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 til dine hjemmearbejdsprojekter | ProHoster","description":"Hvorfor gemme alle data i hukommelsen? Ved datalagring af en webside eller backend vil de fleste fornuftige mennesker f\u00f8rst v\u00e6lge en SQL-database.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}