{"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\/hr\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 .NET in-memory repository pattern za va\u0161e ku\u0107ne projekte","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Za\u0161to pohranjivati sve podatke u memoriji?<\/h2>\n<p><\/p>\n<p>Za pohranu podataka web stranice ili backend-a, ve\u0107ina razumnih ljudi prvo \u0107e odabrati SQL bazu podataka.\u00a0<\/p>\n<p><\/p>\n<p>No, ponekad na pamet do\u0111e misao da model podataka nije prikladan za SQL: na primjer, prilikom izrade pretra\u017eivanja ili dru\u0161tvenog grafa potreban je pretra\u017eiva\u010d za slo\u017eenim vezama izme\u0111u objekata.\u00a0<\/p>\n<p><\/p>\n<p><strong>Najgore je stanje kada radite u timu, a kolega ne zna izraditi brze upite. Koliko ste vremena potro\u0161ili na rje\u0161avanje problema N+1 i na izradu dodatnih indeksa, kako bi SELECT na glavnoj stranici izvodio u razumnom vremenu?<\/strong><\/p>\n<p><\/p>\n<p>Drugi popularan pristup je NoSQL. Prije nekoliko godina bio je veliki hype oko ove teme \u2014 za svaku povoljnu priliku uveli bismo MongoDB i radovali se odgovorima u obliku JSON dokumenata. <em>(Usput, koliko ste \u0161taka morali staviti zbog cikli\u010dnih referenci u dokumentima?)<\/em>.<\/p>\n<p><\/p>\n<p>Predla\u017eem da probamo jo\u0161 jedan alternativni na\u010din \u2014 za\u0161to ne bismo poku\u0161ali pohranjivati sve podatke u memoriji aplikacije, povremeno ih pohranjuju\u0107i u proizvoljno spremi\u0161te (datoteka, udaljena baza podataka)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Memorija je postala jeftina, a svi mogu\u0107i podaci ve\u0107ine malih i srednjih projekata stanu u 1 GB memorije. <em>(Na primjer, moj omiljeni ku\u0107ni projekt \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">financijski pra\u0107enje<\/a><\/noindex>, koji vodi dnevnu statistiku i povijest mojih tro\u0161kova, stanja i transakcija tijekom pola godine, tro\u0161i samo 45 MB memorije.)<\/em><\/p>\n<p><\/p>\n<p>Prednosti:<\/p>\n<p><\/p>\n<ul>\n<li>Pristup podacima postaje jednostavniji \u2014 ne treba se brinuti o upitima, lenjog u\u010ditavanju, osobitostima ORM-a, rad se odvija s obi\u010dnim C# objektima;<\/li>\n<li>Nema problema povezanih s pristupom iz razli\u010ditih niti;<\/li>\n<li>Vrlo je brzo \u2014 nema mre\u017enih upita, izostaje prevo\u0111enje koda u jezik upita, nije potrebno (de)serijaliziranje objekata;<\/li>\n<li>Dopu\u0161teno je pohranjivati podatke u bilo kojem obliku \u2014 bilo u XML-u na disku, bilo u SQL Server-u, bilo u Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Mane:<\/p>\n<p><\/p>\n<ul>\n<li>Gubi se horizontalno skaliranje, a kao posljedica ne mo\u017ee se ostvariti zero downtime deployment;<\/li>\n<li>Ako aplikacija padne \u2014 mo\u017ee se djelomi\u010dno izgubiti podaci. <em>(Ali, na\u0161e aplikacije nikada ne padaju, zar ne?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Kako to radi?<\/h2>\n<p><\/p>\n<p>Algoritam je sljede\u0107i:<\/p>\n<p><\/p>\n<ul>\n<li>Na po\u010detku se uspostavlja veza s spremi\u0161tem podataka i dolazi do u\u010ditavanja podataka;<\/li>\n<li>Gradi se objektni model, primarni indeksi i indeksi odnosa (1:1, 1:vi\u0161e);<\/li>\n<li>Pretpla\u0107ujemo se na promjene svojstava objekata (INotifyPropertyChanged) te na dodavanje ili uklanjanje elemenata iz kolekcije (INotifyCollectionChanged);<\/li>\n<li>Prilikom aktiviranja pretplate \u2014 promijenjeni objekt dodaje se u red za ispis u spremniku podataka;<\/li>\n<li>Periodi\u010dno (uz pomo\u0107 tajmera) u pozadinskom procesu spremaju se promjene u spremnik;<\/li>\n<li>Tako\u0111er, prilikom izlaska iz aplikacije spremaju se promjene u spremnik.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Primer koda<\/h2>\n<p>\n<b class=\"spoiler_title\">Dodajemo potrebne zavisnosti<\/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\">Opisujemo model podataka koji \u0107e se spremati u spremnik<\/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\">Zatim objektni model:<\/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    \/\/ Primjer veze 1:Mnogo\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    \/\/ Pristup s pretragom po indeksu\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\">I kona\u010dno, sama klasa-repozitorij za pristup podacima:<\/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; \/\/ Za testove, omogu\u0107uje ne spremanje promjena u bazu\n    \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n    \n        \/\/ Ako se koristi Hangfire i potrebno je spremiti model podataka za Hangfire u ObjectRepository\n        \/\/ this.RegisterHangfireScheme();  \n    \n        Initialize();\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Stvaramo instancu 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\">Ako \u0107e se u projektu koristiti 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>Umetanje novog objekta:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Prilikom ovog poziva objekt <strong>ParentModel<\/strong> dodaje se i u lokalni ke\u0161, i u red za zapis u bazu. Stoga ova operacija traje O(1), a s ovim objektom se mo\u017ee odmah raditi.<\/p>\n<p><\/p>\n<p>Na primjer, da prona\u0111ete ovaj objekt u repozitoriju i uvjerite se da je vra\u0107eni objekt isti primjerak:<\/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>\u0160to se pritom doga\u0111a? <em>Set()<\/em> vra\u0107a <em>TableDictionary<\/em>, koji sadr\u017ei <em>ConcurrentDictionary<\/em> i pru\u017ea dodatne funkcionalnosti primarnih i sekundarnih indeksa. To omogu\u0107ava metode za pretra\u017eivanje prema Id-u (ili drugim proizvoljnim korisni\u010dkim indeksima) bez potpunog pretra\u017eivanja svih objekata.<\/p>\n<p><\/p>\n<p>Prilikom dodavanja objekata u <em>ObjectRepository<\/em> dodaje se pretplata na promjenu njihovih svojstava, pa bilo koja promjena svojstava tako\u0111er dovodi do dodavanja ovog objekta u red za zapis.\u00a0<br \/>\nA\u017euriranje svojstava s vanjske strane izgleda isto kao i rad s POCO objektom:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"A\u017eurirana vrijednost\";<\/code><\/pre>\n<p><\/p>\n<p>Objekt se mo\u017ee ukloniti na sljede\u0107e 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>Pritom se tako\u0111er doga\u0111a dodavanje objekta u red za uklanjanje.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Kako radi spremanje?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> prilikom promjene pra\u0107enih objekata (kao dodavanje ili uklanjanje, tako i promjena svojstava) pokre\u0107e doga\u0111aj <em>ModelChanged<\/em>, na koji se pretpla\u0107uje <em>IStorage<\/em>. Implementacije <em>IStorage<\/em> prilikom nastanka doga\u0111aja <em>ModelChanged<\/em> nakupljaju promjene u 3 reda \u2013 za dodavanje, za a\u017euriranje i za uklanjanje.<\/p>\n<p><\/p>\n<p>Tako\u0111er implementacije <em>IStorage<\/em> prilikom inicijalizacije kreiraju mjera\u010d vremena koji svake 5 sekundi pokre\u0107e spremanje promjena.\u00a0<\/p>\n<p><\/p>\n<p><em>Osim toga, postoji API za prisilno pozivanje spremanja: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Prije svakog spremanja prvo se uklanjaju iz redova besmislene operacije (na primjer duplici doga\u0111aja \u2013 kada se objekt mijenjao dvaput ili brzo dodavanje\/uklanjanje objekata), a tek potom samo spremanje.\u00a0<\/p>\n<p><\/p>\n<p><em>U svim slu\u010dajevima sprema se aktualni objekt u cijelosti, pa je mogu\u0107a situacija da se objekti spremaju u drugom redoslijedu nego \u0161to su se mijenjali, pa \u010dak mogu biti spremljene novije verzije objekata nego u trenutku dodavanja u red.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">\u0160to je jo\u0161?<\/h2>\n<p><\/p>\n<ul>\n<li>Sve biblioteke temelje se na .NET Standard 2.0. Mogu se koristiti u bilo kojem modernom .NET projektu.<\/li>\n<li>API je thread-safe. Interni kolekcije su implementirane na bazi <em>ConcurrentDictionary<\/em>, a handleri doga\u0111aja imaju ili blokade ili ih ne trebaju.\u00a0<br \/>\nJedina stvar o kojoj treba voditi ra\u010duna - prilikom zatvaranja aplikacije pozvati <em>ObjectRepository.Save();<\/em><\/li>\n<li>Prilagodljivi indeksi (zahtijevaju jedinstvenost):<\/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\">Tko to koristi?<\/h2>\n<p><\/p>\n<p>Osobno, ja sam po\u010deo koristiti ovaj pristup u svim svojim hobi projektima, jer je prakti\u010dan, i ne zahtijeva velike tro\u0161kove pisanja sloja za pristup podacima ili postavljanja te\u0161ke infrastrukture. Osobno mi obi\u010dno sufficira pohrana podataka u litedb ili u datoteci.\u00a0<\/p>\n<p><\/p>\n<p>No, u pro\u0161losti, kada smo s tim timom radili na sada ve\u0107 nepostoje\u0107em startapu EscapeTeams (<em>mislio sam da su tu novci - ali ne, opet iskustvo<\/em>) - koristili smo Azure Table Storage za pohranu podataka.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Planovi za budu\u0107nost<\/h2>\n<p><\/p>\n<p>\u017deli se rije\u0161iti jedan od glavnih nedostataka ovog pristupa \u2014 horizontalno skaliranje. Za to su potrebne ili distribuirane transakcije (sic!), ili odlu\u010diti da se isti podaci iz razli\u010ditih instanci ne smiju mijenjati, ili neka se mijenjaju po principu \u201etko zadnji, taj je u pravu\u201c.<\/p>\n<p><\/p>\n<p>S tehni\u010dkog stajali\u0161ta, vidim mogu\u0107om sljede\u0107u shemu:<\/p>\n<p><\/p>\n<ul>\n<li>Pohraniti umjesto objektne modele EventLog i Snapshot<\/li>\n<li>Prona\u0107i druge instance (dodati u postavke krajnje to\u010dke svih instanci? udp otkrivanje? master\/slave?)<\/li>\n<li>Replikacija izme\u0111u instanci EventLog-a putem nekog od algoritama konsenzusa, na primjer RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Postoji i jo\u0161 jedan problem koji me brine - kaskadno brisanje, ili otkrivanje slu\u010dajeva brisanja objekata na koje postoje reference iz drugih objekata.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Izvorni kod<\/h2>\n<p><\/p>\n<p>Ako ste pro\u010ditali do ovdje - preostaje samo \u010ditati kod, koji mo\u017eete prona\u0107i 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\/hr\/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=\"hr_HR\" \/>\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\/hr\/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 za va\u0161e ku\u0107ne projekte | ProHoster","description":"Za\u0161to pohranjivati sve podatke u memoriji? Za pohranu podataka web stranice ili backenda prvo \u0161to ve\u0107ina zdravih razuma ljudi odabire je SQL baza podataka.","canonical_url":"https:\/\/prohoster.info\/hr\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"hr_HR","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\/hr\/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\/hr\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}