{"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\/novosti-interneta\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 pattern di repository in-memory .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, la maggior parte delle persone sensate sceglier\u00e0 prima di tutto un database SQL.\u00a0<\/p>\n<p><\/p>\n<p>Ma a volte viene in mente che il modello di dati non \u00e8 adatto per SQL: ad esempio, nella creazione di una ricerca o di un grafo sociale \u00e8 necessario cercare relazioni complesse tra gli oggetti.\u00a0<\/p>\n<p><\/p>\n<p><strong>La situazione peggiore si verifica quando si lavora in team e un collega non sa costruire query veloci. Quanto tempo hai speso a risolvere i problemi N+1 e a creare indici supplementari affinch\u00e9 il SELECT nella homepage funzionasse in un tempo ragionevole?<\/strong><\/p>\n<p><\/p>\n<p>Un altro approccio popolare \u00e8 NoSQL. Qualche anno fa c'era un grande hype attorno a questo tema: per ogni occasione conveniente si distribuiva MongoDB e si gioiva delle risposte in formato json. <em>(tra l'altro, quanti stratagemmi hanno dovuto essere inseriti a causa dei collegamenti circolari nei documenti?)<\/em>.<\/p>\n<p><\/p>\n<p>Propongo di provare un altro metodo alternativo: perch\u00e9 non provare a memorizzare tutti i dati nella memoria dell'applicazione, salvando periodicamente in un'archiviazione arbitraria (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 dato dei maggiori progetti piccoli e medi pu\u00f2 facilmente entrare in 1 GB di memoria. <em>(Ad esempio, il mio progetto domestico preferito \u00e8 il <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">tracker finanziario<\/a><\/noindex>, che tiene traccia della mia statistica quotidiana e della storia delle mie spese, dei saldi e delle transazioni per un anno e mezzo, e consuma solo 45 MB di memoria.)<\/em><\/p>\n<p><\/p>\n<p>Vantaggi:<\/p>\n<p><\/p>\n<ul>\n<li>L'accesso ai dati diventa pi\u00f9 semplice: non \u00e8 necessario preoccuparsi delle query, del caricamento lento, delle particolarit\u00e0 dell'ORM, il lavoro avviene con normali oggetti C#;<\/li>\n<li>Nessun problema legato all'accesso da thread diversi;<\/li>\n<li>Molto veloce: non ci sono richieste di rete, non c'\u00e8 traduzione del codice in linguaggio di query, non \u00e8 necessaria la (de)serializzazione degli oggetti;<\/li>\n<li>\u00c8 possibile memorizzare i dati in qualsiasi formato: sia in XML su disco, sia in SQL Server, sia 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 il deployment senza downtime;<\/li>\n<li>Se l'applicazione si blocca, si possono perdere parzialmente i dati. <em>(Ma la nostra applicazione non si blocca mai, vero?)<\/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 il repository dati e i dati vengono caricati;<\/li>\n<li>Viene costruito un modello ad oggetti, indici primari e indici delle relazioni (1:1, 1:Molti);<\/li>\n<li>Si crea un abbonamento alle modifiche delle propriet\u00e0 degli oggetti (INotifyPropertyChanged) e all'aggiunta o rimozione di elementi nella collezione (INotifyCollectionChanged);<\/li>\n<li>Quando si verifica l'abbonamento, l'oggetto modificato viene aggiunto alla coda per la scrittura nel database;<\/li>\n<li>Periodicamente (tramite un timer) vengono salvate in un thread in background le modifiche nel database;<\/li>\n<li>Al momento della chiusura dell'app, vengono salvate anche le modifiche nel database.<\/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\">Definiamo il modello dati che sar\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\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    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    \/\/ Esempio di relazione 1:Molti\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    \/\/ Accesso con ricerca per indice\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\">E infine la classe repository per l'accesso ai dati:<\/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; \/\/ Per i test, consente di non salvare le modifiche nel database\n        \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n        \n        \/\/ Se si utilizza Hangfire e necessario memorizzare il modello di dati per Hangfire in ObjectRepository\n        \/\/ this.RegisterHangfireScheme();\n        \n        Initialize();\n    }\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    \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    services.AddHangfire(s =&gt; s.UseHangfireStorage(objectRepository));\n}<\/code><\/pre>\n<p><\/p>\n<p>Inserire 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 alla cache locale sia in coda per la registrazione nel database. Pertanto, questa operazione richiede O(1) e si pu\u00f2 subito 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 lo stesso esemplare:<\/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> restituisce <em>TableDictionary<\/em>, che contiene <em>ConcurrentDictionary<\/em> e fornisce funzionalit\u00e0 aggiuntive per gli indici primari e secondari. Questo consente di avere metodi per la ricerca per Id (o altri indici personalizzati arbitrari) senza dover eseguire una scansione completa di tutti gli oggetti.<\/p>\n<p><\/p>\n<p>Quando si aggiungono oggetti a <em>ObjectRepository<\/em> viene aggiunta un'iscrizione per le modifiche alle loro propriet\u00e0, quindi qualsiasi modifica delle propriet\u00e0 comporta anche l'aggiunta di questo oggetto a una coda di 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>\u00c8 possibile rimuovere un oggetto 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>Anche in questo caso, l'oggetto viene aggiunto 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> la modifica degli oggetti monitorati (come l'aggiunta o la rimozione e la modifica delle propriet\u00e0) genera un evento <em>ModelChanged<\/em>, a cui \u00e8 iscritto <em>IStorage<\/em>. Le implementazioni <em>IStorage<\/em> quando si verifica l'evento <em>ModelChanged<\/em> accumulano le modifiche in 3 code \u2014 per aggiunta, per aggiornamento e per rimozione.<\/p>\n<p><\/p>\n<p>Le implementazioni forniscono <em>IStorage<\/em> Durante l'inizializzazione, viene creato un timer che chiama il salvataggio delle modifiche ogni 5 secondi.\u00a0<\/p>\n<p><\/p>\n<p><em>Esiste anche un'API per forzare il salvataggio: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Prima di ogni salvataggio, vengono rimossi dalle code le operazioni inutili (ad esempio, i duplicati degli eventi \u2014 quando un oggetto \u00e8 stato modificato due volte o ci sono stati aggiustamenti rapidi di oggetti), e solo dopo avviene il salvataggio stesso.\u00a0<\/p>\n<p><\/p>\n<p><em>In tutti i casi, viene salvato l'oggetto attuale per intero, quindi \u00e8 possibile che gli oggetti vengano salvati in un ordine diverso rispetto a quello in cui sono stati modificati, inclusa la possibilit\u00e0 che versioni pi\u00f9 recenti degli oggetti vengano salvate rispetto al momento dell'aggiunta in coda.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Cosa c'\u00e8 di nuovo?<\/h2>\n<p><\/p>\n<ul>\n<li>Tutte le librerie si basano 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 basate su <em>ConcurrentDictionary<\/em>, i gestori di eventi utilizzano blocchi o non ne hanno bisogno.\u00a0<br \/>\nL'unica cosa da ricordare \u00e8 che, alla chiusura dell'applicazione, \u00e8 necessario chiamare <em>ObjectRepository.Save();<\/em><\/li>\n<li>Indici arbitrari (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 usare questo approccio in tutti i miei progetti hobbistici, perch\u00e9 \u00e8 comodo e non richiede grandi investimenti per la scrittura di uno strato di accesso ai dati o per sviluppare un'infrastruttura pesante. Di solito per me \u00e8 sufficiente memorizzare i dati in litedb o in un file.\u00a0<\/p>\n<p><\/p>\n<p>Ma in passato, quando con il team abbiamo creato la ormai defunta startup EscapeTeams (<em>pensavo ecco, i soldi \u2014 e invece niente, solo esperienza<\/em>) \u2014 abbiamo usato Azure Table Storage per memorizzare i dati.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Piani per il futuro<\/h2>\n<p><\/p>\n<p>Si desidera risolvere uno dei principali svantaggi di questo approccio: la scalabilit\u00e0 orizzontale. Per questo sono necessari o transazioni distribuite (sic!), o prendere una decisione risoluta che gli stessi dati da diverse istanze non debbano cambiare, oppure che possano cambiare secondo il principio di \"chi arriva ultimo, arriva meglio\".<\/p>\n<p><\/p>\n<p>Dal punto di vista tecnico, vedo possibile il seguente schema:<\/p>\n<p><\/p>\n<ul>\n<li>Memorizzare invece del modello oggetto EventLog e Snapshot<\/li>\n<li>Trovare altre istanze (inserire nelle impostazioni gli endpoint di tutte le istanze? discovery 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'individuazione 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 fin qui, rimane solo da leggere 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-novosti-interneta"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - 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. \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. \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,\" \/>\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\/novosti-interneta\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\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. \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. \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,\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/novosti-interneta\/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 repository in-memory per .NET per i tuoi progetti domestici | ProHoster","description":"Perch\u00e9 conservare tutti i dati in memoria? Per memorizzare i dati di un sito o di un backend, la maggior parte delle persone di buon senso opterebbe per un database SQL. Ma a volte viene il pensiero che il modello dati non si adatti a SQL: ad esempio, nella costruzione di una ricerca o di un grafo sociale \u00e8 necessario cercare attraverso relazioni complesse tra oggetti. La situazione \u00e8 peggiore quando si lavora in team.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/novosti-interneta\/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. \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. \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,","og:url":"https:\/\/prohoster.info\/it\/blog\/novosti-interneta\/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"},"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}]}}