{"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\/it\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 pattern di repository in memoria .NET per i tuoi progetti domestici","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Perch\u00e9 conservare tutti i dati in memoria?<\/h2>\n<p><\/p>\n<p>Per memorizzare i dati del sito o del backend, il primo desiderio della maggior parte delle persone sensate sar\u00e0 scegliere un database SQL.\u00a0<\/p>\n<p><\/p>\n<p>Ma a volte viene in mente che il modello dei dati non si adatta a SQL: ad esempio, quando si costruisce una ricerca o un grafo sociale \u00e8 necessaria una ricerca su relazioni complesse tra oggetti.\u00a0<\/p>\n<p><\/p>\n<p><strong>La situazione peggiore \u00e8 quando lavori in team e un collega non sa costruire query veloci. Quanto tempo hai speso per risolvere i problemi N+1 e per costruire indici aggiuntivi affinch\u00e9 il SELECT sulla home page funzionasse in un tempo ragionevole?<\/strong><\/p>\n<p><\/p>\n<p>Un altro approccio popolare \u00e8 NoSQL. Qualche anno fa c'era un gran clamore attorno a questo tema: per ogni occasione favorevole veniva implementata MongoDB e ci si rallegrava per le risposte in forma di documenti JSON. <em>(Tra l'altro, quanti workaround hai dovuto inserire a causa dei riferimenti circolari nei documenti?)<\/em>.<\/p>\n<p><\/p>\n<p>Ti propongo di provare un altro modo, alternativo: perch\u00e9 non provare a memorizzare tutti i dati in memoria dell'applicazione, salvando periodicamente in uno storage arbitrario (file, database remoto)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>La memoria \u00e8 diventata economica e qualsiasi possibile dato della maggior parte dei progetti piccoli e medi pu\u00f2 stare in 1 GB di memoria. <em>(Ad esempio, il mio progetto domestico preferito \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">un tracker finanziario<\/a><\/noindex>, che tiene traccia delle statistiche giornaliere e della storia delle mie spese, dei bilanci e delle transazioni per un anno e mezzo consuma solo 45 MB di memoria.)<\/em><\/p>\n<p><\/p>\n<p>Pro:<\/p>\n<p><\/p>\n<ul>\n<li>L'accesso ai dati diventa pi\u00f9 semplice: non \u00e8 necessario preoccuparsi delle query, del caricamento pigro, delle peculiarit\u00e0 dell'ORM, il lavoro avviene con normali oggetti C#;<\/li>\n<li>Non ci sono problemi legati all'accesso da thread diversi;<\/li>\n<li>Molto veloce: non ci sono richieste di rete, manca la traduzione del codice nel linguaggio di query, non \u00e8 necessaria la (de)serializzazione degli oggetti;<\/li>\n<li>\u00c8 accettabile memorizzare i dati in qualsiasi forma: anche in XML su disco, in SQL Server, o in Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Contro:<\/p>\n<p><\/p>\n<ul>\n<li>Si perde la scalabilit\u00e0 orizzontale e, di conseguenza, non \u00e8 possibile effettuare un deployment a zero downtime;<\/li>\n<li>Se l'applicazione va in crash, \u00e8 possibile perdere parzialmente i dati. <em>(Ma la nostra applicazione non va mai in crash, giusto?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Come funziona?<\/h2>\n<p><\/p>\n<p>L'algoritmo \u00e8 il seguente:<\/p>\n<p><\/p>\n<ul>\n<li>All'avvio viene stabilita una connessione con lo storage dei dati e avviene il caricamento dei dati;<\/li>\n<li>Si costruisce il modello ad oggetti, gli indici primari e gli indici delle relazioni (1:1, 1:M);<\/li>\n<li>Viene creata un'abbonamento per le modifiche delle propriet\u00e0 degli oggetti (INotifyPropertyChanged) e per l'aggiunta o la rimozione di elementi nella collezione (INotifyCollectionChanged);<\/li>\n<li>Quando si attiva l'abbonamento, l'oggetto modificato viene aggiunto alla coda per la scrittura nel database;<\/li>\n<li>Periodicamente (tramite un timer), le modifiche vengono salvate nel database in un thread secondario;<\/li>\n<li>Le modifiche vengono salvate nel database anche all'uscita dal programma.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Esempio di codice<\/h2>\n<p>\n<b class=\"spoiler_title\">Aggiungiamo le dipendenze necessarie<\/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\">Descriviamo il modello dei dati che verr\u00e0 salvato nel database<\/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\">Poi il modello degli oggetti:<\/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 \/\/ Esempio di relazione 1:Molti\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    \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 \/\/ Accesso con ricerca per indice\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\">E infine la classe-repository per l'accesso ai dati:<\/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; \/\/ Per i test, consente di non salvare le modifiche nel database\n\u00a0 \u00a0 \n\u00a0 \u00a0 \u00a0 \u00a0 AddType((ParentEntity x) =&gt; new ParentModel(x));\n\u00a0 \u00a0 \u00a0 \u00a0 AddType((ChildEntity x) =&gt; new ChildModel(x));\n\u00a0 \u00a0 \n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ Se viene utilizzato Hangfire e \u00e8 necessario memorizzare il modello dei dati per Hangfire in ObjectRepository\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ this.RegisterHangfireScheme();  \n\u00a0 \u00a0 \n\u00a0 \u00a0 \u00a0 \u00a0 Initialize();\n\u00a0 \u00a0 }\n}<\/code><\/pre>\n<p><\/p>\n<p>Creiamo un'istanza di 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\">Se nel progetto verr\u00e0 utilizzato 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>Inserimento di un nuovo oggetto:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Con questa chiamata l'oggetto <strong>ParentModel<\/strong> viene aggiunto sia nella cache locale che nella coda per la scrittura nel database. Pertanto, questa operazione richiede O(1), e si pu\u00f2 immediatamente lavorare con questo oggetto.<\/p>\n<p><\/p>\n<p>Ad esempio, per trovare questo oggetto nel repository e assicurarsi che l'oggetto restituito sia esattamente lo stesso oggetto:<\/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>Cosa succede in questo caso? <em>Set()<\/em> false, se l'elemento da sostituire \u00e8 <em>TableDictionary<\/em>, che contiene <em>ConcurrentDictionary<\/em> e fornisce funzionalit\u00e0 aggiuntive per indici primari e secondari. Questo consente di avere metodi per la ricerca per Id (o altri indici personalizzati) senza dover esaminare tutti gli oggetti.<\/p>\n<p><\/p>\n<p>Quando si aggiungono oggetti a <em>ObjectRepository<\/em> viene aggiunta un'iscrizione alle modifiche delle loro propriet\u00e0, quindi qualsiasi modifica avr\u00e0 anche come conseguenza l'aggiunta di questo oggetto alla coda per la scrittura.\u00a0<br \/>\nL'aggiornamento delle propriet\u00e0 dall'esterno appare esattamente come il lavoro con un oggetto POCO:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Valore aggiornato\";<\/code><\/pre>\n<p><\/p>\n<p>Un oggetto pu\u00f2 essere rimosso nei seguenti modi:<\/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>In questo caso, avviene anche l'aggiunta dell'oggetto alla coda di rimozione.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Come funziona il salvataggio?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> quando si apportano modifiche agli oggetti monitorati (sia aggiunta o rimozione che modifica delle propriet\u00e0) genera un evento <em>ModelChanged<\/em>, a cui \u00e8 iscritta <em>IStorage<\/em>. Le implementazioni <em>IStorage<\/em> in caso di evento <em>ModelChanged<\/em> accumulano le modifiche in 3 code: per aggiunta, per aggiornamento e per rimozione.<\/p>\n<p><\/p>\n<p>Inoltre, le implementazioni <em>IStorage<\/em> durante l'inizializzazione creano un timer, che ogni 5 secondi esegue il salvataggio delle modifiche.\u00a0<\/p>\n<p><\/p>\n<p><em>In aggiunta, esiste un'API per forzare il salvataggio: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Prima di ogni salvataggio, si rimuovono prima le operazioni insignificanti dalle code (ad esempio duplicati di eventi - quando un oggetto \u00e8 stato modificato due volte o una rapida aggiunta\/rimozione di oggetti) e solo successivamente avviene il salvataggio vero e proprio.\u00a0<\/p>\n<p><\/p>\n<p><em>In tutti i casi, viene salvato l'oggetto attuale nella sua interezza, quindi \u00e8 possibile che gli oggetti vengano salvati in un ordine diverso rispetto a quanto modificati, inclusa la possibilit\u00e0 che versioni pi\u00f9 recenti degli oggetti vengano salvate rispetto al momento in cui sono stati aggiunti alla coda.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Che cos'altro c'\u00e8?<\/h2>\n<p><\/p>\n<ul>\n<li>Tutte le librerie sono basate su .NET Standard 2.0. Possono essere utilizzate in qualsiasi progetto .NET moderno.<\/li>\n<li>L'API \u00e8 thread-safe. Le collezioni interne sono implementate su base <em>ConcurrentDictionary<\/em>, gli handler degli eventi hanno sia blocchi che non ne hanno bisogno.\u00a0<br \/>\nL'unica cosa da ricordare \u00e8 che al termine dell'applicazione bisogna chiamare <em>ObjectRepository.Save();<\/em><\/li>\n<li>Indici arbitrarie (richiedono unicit\u00e0):<\/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\">Chi lo utilizza?<\/h2>\n<p><\/p>\n<p>Personalmente, ho iniziato a utilizzare questo approccio in tutti i progetti hobby, perch\u00e9 \u00e8 comodo e non richiede grandi sforzi nella scrittura di uno strato di accesso ai dati o nel settaggio di un'infrastruttura pesante. Di solito, per me \u00e8 sufficiente conservare i dati in litedb o in un file.\u00a0<\/p>\n<p><\/p>\n<p>Ma in passato, quando con il mio team abbiamo avviato la ormai defunta startup EscapeTeams (<em>pensavo che ecco i soldi \u2014 ma no, di nuovo esperienze<\/em>) \u2014 abbiamo utilizzato Azure Table Storage per la memorizzazione dei dati.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Piani futuri<\/h2>\n<p><\/p>\n<p>Si desidera risolvere uno dei principali svantaggi di questo approccio: la scalabilit\u00e0 orizzontale. Per questo sono necessarie transazioni distribuite (sic!), oppure \u00e8 necessario prendere una decisione ferma che gli stessi dati da diverse istanze non debbano cambiare, oppure possono cambiare secondo il principio 'chi arriva ultimo ha ragione'.<\/p>\n<p><\/p>\n<p>Da un punto di vista tecnico vedo possibile il seguente schema:<\/p>\n<p><\/p>\n<ul>\n<li>Conservare invece del modello ad oggetti EventLog e Snapshot<\/li>\n<li>Trovare altre istanze (aggiungere nelle impostazioni i punti finali di tutte le istanze? scoperta UDP? master\/slave?)<\/li>\n<li>Replicare tra le istanze EventLog tramite uno qualsiasi degli algoritmi di consenso, ad esempio RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>C'\u00e8 anche un altro problema che mi preoccupa: la cancellazione a cascata o l'identificazione dei casi di eliminazione di oggetti a cui ci sono riferimenti da altri oggetti.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Codice sorgente<\/h2>\n<p><\/p>\n<p>Se sei arrivato fino a qui, rimane da leggere solo il codice, che puoi trovare su GitHub:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>Fonte: <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.1.1 - 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\/it\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\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\/it\/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 pattern di repository in-memory per i tuoi progetti domestici | ProHoster","description":"Perch\u00e9 conservare tutti i dati in memoria? Per la memorizzazione dei dati di un sito o backend, la prima scelta della maggior parte delle persone di buon senso sar\u00e0 un database SQL.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","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\/it\/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\/it\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}