{"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\/sl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 .NET in-memory repository obrazac za va\u0161e li\u010dne projekte","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Zakaj hraniti vse podatke v pomnilniku?<\/h2>\n<p><\/p>\n<p>Za shranjevanje podatkov spletnega mesta ali ozadja ve\u010dina razumnih ljudi najprej izbere SQL bazo podatkov.\u00a0<\/p>\n<p><\/p>\n<p>V\u010dasih pa pride na misel, da model podatkov morda ni primeren za SQL: na primer, pri gradnji iskalnika ali dru\u017ebenega grafikon lahko potrebujete iskanje preko zapletenih povezav med objekti.\u00a0<\/p>\n<p><\/p>\n<p><strong>Najslab\u0161e je, ko delate v ekipi in kolega ne zna graditi hitrih poizvedb. Koliko \u010dasa ste porabili za re\u0161evanje te\u017eav N+1 in za gradnjo dodatnih indeksov, da je SELECT na glavni strani deloval v razumnem \u010dasu?<\/strong><\/p>\n<p><\/p>\n<p>Drug priljubljen pristop je NoSQL. Pred nekaj leti je bilo ob tej temi veliko vznemirjenja \u2014 za vsak ugoden primer smo odprli MongoDB in bili veseli odgovorov v obliki json-dokumentov. <em>(mimogrede, koliko bole\u010dih re\u0161itev je bilo potrebnih zaradi obro\u010dnih povezav v dokumentih?)<\/em>.<\/p>\n<p><\/p>\n<p>Predlagam, da poskusite \u0161e en, alternativen na\u010din \u2014 zakaj ne bi hranili vseh podatkov v pomnilniku aplikacije, ob\u010dasno pa jih shranili v poljuben skladi\u0161\u010de (datoteka, oddaljena baza podatkov)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Pomnilnik je postal poceni, ve\u010dina mogo\u010dih podatkov manj\u0161ih in srednje velikih projektov se prilega v 1 GB pomnilnika. <em>(Na primer, moj najljub\u0161i doma\u010di projekt \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">finan\u010dni sledilnik<\/a><\/noindex>, ki vodi dnevno statistiko in zgodovino mojih stro\u0161kov, stanj in transakcij v zadnjem letu in pol, porabi le 45 MB pomnilnika.)<\/em><\/p>\n<p><\/p>\n<p>Prednosti:<\/p>\n<p><\/p>\n<ul>\n<li>Dostop do podatkov postane enostavnej\u0161i \u2014 ni treba skrbeti za poizvedbe, leno nalaganje, posebnosti ORM, delo poteka z obi\u010dajnimi C# objekti;<\/li>\n<li>Ni te\u017eav, povezanih z dostopom iz razli\u010dnih niti;<\/li>\n<li>Zelo hitro \u2014 ni omre\u017enih poizvedb, odsotnost prevajanja kode v jezik poizvedb, ni potrebno (de)seriliziranje objektov;<\/li>\n<li>Dovoljena je shranitev podatkov v katerikoli obliki \u2014 bodisi v XML na disku, bodisi v SQL Serverju, bodisi v Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Mane:<\/p>\n<p><\/p>\n<ul>\n<li>Zgubljeno je horizontalno skaliranje, in kot posledica tega ni mogo\u010de izvesti zero downtime implementacije;<\/li>\n<li>\u010ce aplikacija pade \u2014 lahko delno izgubimo podatke. <em>(Ampak na\u0161a aplikacija nikoli ne pade, kajne?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Kako deluje?<\/h2>\n<p><\/p>\n<p>Algoritem je naslednji:<\/p>\n<p><\/p>\n<ul>\n<li>Ob zagonu se vzpostavi povezava s podatkovnim skladi\u0161\u010dem in nalo\u017eijo se podatki;<\/li>\n<li>Gradi se objektni model, primarni indeksi in indeksi odnosov (1:1, 1:Many);<\/li>\n<li>Ustvarja se naro\u010dnina na spremembe lastnosti objektov (INotifyPropertyChanged) in na dodajanje ali odstranjevanje elementov v zbirki (INotifyCollectionChanged);<\/li>\n<li>Ob aktivaciji naro\u010dnine se spremenjeni objekt doda v vrsto za zapis v podatkovno shrambo;<\/li>\n<li>Ob\u010dasno (po \u010dasovniku) se v ozadju shranijo spremembe v shrambi;<\/li>\n<li>Ob zapustitvi aplikacije se tudi shranijo spremembe v shrambi.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Primer kode<\/h2>\n<p>\n<b class=\"spoiler_title\">Dodamo potrebne odvisnosti<\/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\">Opi\u0161emo podatkovni model, ki bo shranjen v shrambi<\/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\">Nato objektni model:<\/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 \/\/ Primer povezave 1:Many\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 \/\/ Dostop s iskanjem po indeksu\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\">In kon\u010dno razred repozitorija za dostop do podatkov:<\/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; \/\/ Za teste, omogo\u010da, da ne shranjujemo sprememb v bazo\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 \/\/ \u010ce se uporablja Hangfire in je potrebno hraniti model podatkov za Hangfire v 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>Ustvarimo primerek 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\u00a0 \u00a0 \nvar repository = new MyObjectRepository(dbStorage);\nawait repository.WaitForInitialize();<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">\u010ce se v projektu uporablja HangFire<\/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>Vstavitev novega objekta:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Ob tem klicu je objekt <strong>ParentModel<\/strong> se dodaja tako v lokalno predpomnilnik kot v vrsto za zapis v bazo. Zato ta operacija traja O(1), in s tem objektom lahko takoj delamo.<\/p>\n<p><\/p>\n<p>Na primer, da najdete ta objekt v repozitoriju in se prepri\u010date, da je vrnjen objekt istega primera:<\/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>Kaj se pri tem zgodi? <em>Set()<\/em> vra\u010dajo <em>TableDictionary<\/em>, ki vsebuje <em>ConcurrentDictionary<\/em> in zagotavlja dodatno funkcionalnost primarnih in sekundarnih indeksov. To omogo\u010da imeti metode za iskanje po Id (ali drugih poljubnih uporabni\u0161kih indeksih) brez popolnega pre\u010desavanja vseh objektov.<\/p>\n<p><\/p>\n<p>Pri dodajanju objektov v <em>ObjectRepository<\/em> se doda naro\u010dnina za spremembo njihovih lastnosti, zato vsaka sprememba lastnosti povzro\u010di tudi dodatek tega objekta v vrsto za zapis.\u00a0<br \/>\nPosodobitev lastnosti od zunaj izgleda enako kot delo s POCO objektom:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Posodobljena vrednost\";<\/code><\/pre>\n<p><\/p>\n<p>Objekt lahko odstranite na naslednje na\u010dine:<\/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>Pri tem se tudi dodaja objekt v vrsto za odstranjevanje.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Kako deluje shranjevanje?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> pri spremembah spremljanih objektov (kot dodajanje ali odstranjevanje ter sprememba lastnosti) spro\u017ei dogodek <em>ModelChanged<\/em>, na katerega je naro\u010den <em>IStorage<\/em>. Implementacije <em>IStorage<\/em> ob nastanka dogodka <em>ModelChanged<\/em> shranijo spremembe v 3 vrste \u2014 za dodajanje, posodobitev in odstranjevanje.<\/p>\n<p><\/p>\n<p>Implementacije <em>IStorage<\/em> ob inicializaciji ustvarijo \u010dasovnik, ki vsakih 5 sekund spro\u017ei shranjevanje sprememb.\u00a0<\/p>\n<p><\/p>\n<p><em>Obstaja tudi API za prisilno spro\u017eitev shranjevanja: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Pred vsakim shranjevanjem se najprej odstranijo iz vrst brezsmiselne operacije (na primer podvojitve dogodkov \u2014 ko se je objekt spremenil dvakrat ali hitro dodajanje\/odstranjevanje objektov), \u0161ele nato samo shranjevanje.\u00a0<\/p>\n<p><\/p>\n<p><em>V vseh primerih se shrani aktualen objekt v celoti, zato je mo\u017ena situacija, ko se objekti shranijo v druga\u010dnem vrstnem redu, kot so se spreminjali, vklju\u010dno z mo\u017enostjo shranjevanja novej\u0161ih razli\u010dic objektov, kot so bile ob dodajanju v vrsto.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">\u0160ta je jo\u0161?<\/h2>\n<p><\/p>\n<ul>\n<li>Vse knji\u017enice temeljijo na .NET Standard 2.0. Lahko se uporabljajo v katerem koli sodobnem .NET projektu.<\/li>\n<li>API je niti varno. Notranje zbirke so izvedene na osnovi <em>ConcurrentDictionary<\/em>, obravnava dogodkov ima bodisi zaklepe bodisi jih ne potrebuje.\u00a0<br \/>\nEno, kar je treba zapomniti \u2014 pri zaklju\u010dku aplikacije pokli\u010dite <em>ObjectRepository.Save();<\/em><\/li>\n<li>Poljubni indeksi (zahtevajo edinstvenost):<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"cs\">repository.Set().AddIndex(x =&gt; x.Value);\nrepository.Set().Find(x =&gt; x.Value, \"myValue\");<\/code><\/pre>\n<p><\/p>\n<h2 id=\"kto-eto-ispolzuet\">Kdo to uporablja?<\/h2>\n<p><\/p>\n<p>Osebno sem za\u010del uporabljati ta pristop pri vseh svojih hobi projektih, ker je priro\u010dno in ne zahteva veliko napora za pisanje sloja za dostop do podatkov ali uvajanje te\u017eke infrastrukture. Zame je obi\u010dajno dovolj, da podatke hranim v litedb ali datoteki.\u00a0<\/p>\n<p><\/p>\n<p>Toda v preteklosti, ko smo s timom delali zdaj \u017ee umrli startup EscapeTeams (<em>sem mislil, da so tukaj, denar \u2014 pa ni, spet izku\u0161nje<\/em>) \u2014 smo za shranjevanje podatkov uporabljali Azure Table Storage.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Na\u010drti za prihodnost<\/h2>\n<p><\/p>\n<p>\u017delim odpraviti eno izmed glavnih pomanjkljivosti tega pristopa \u2014 horizontalno skaliranje. Za to so potrebne ali porazdeljene transakcije (sic!), ali sprejeti odlo\u010ditev, da se isti podatki iz razli\u010dnih instanc ne smejo spreminjati, ali pa naj se spreminjajo po na\u010delu &#171;kdo zadnji \u2014 tisti ima prav&#187;.<\/p>\n<p><\/p>\n<p>Z vidika tehnike vidim naslednjo shemo:<\/p>\n<p><\/p>\n<ul>\n<li>Shranjujem namesto objektnega modela EventLog in Snapshot<\/li>\n<li>Najti druge instance (dodati kon\u010dne to\u010dke vseh instanc v nastavitve? udp odkrivanje? master\/slave?)<\/li>\n<li>Replikacija med instancami EventLog preko katerega koli od konsenznih algoritmov, na primer RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Obstaja tudi \u0161e en problem, ki me skrbi \u2014 to je kaskadno brisanje ali odkrivanje primerov brisanja objektov, na katere so sklici iz drugih objektov.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Izvorna koda<\/h2>\n<p><\/p>\n<p>\u010ce ste do sem prebrali \u2014 ostane le \u0161e branje kode, ki jo lahko najdete na GitHubu:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>Izvor: <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.4 - 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\/sl\/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.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sl_SI\" \/>\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\/sl\/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 v pomnilniku vzorec repozitorija za va\u0161e doma\u010de projekte | ProHoster","description":"Zakaj hraniti vse podatke v pomnilniku? Pri shranjevanju podatkov spletne strani ali backend-a bi prva \u017eelja ve\u010dine razumen ljudi bila SQL baza podatkov.","canonical_url":"https:\/\/prohoster.info\/sl\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sl_SI","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\/sl\/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\/sl\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}