{"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\/pt\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 padr\u00e3o de reposit\u00f3rio em mem\u00f3ria .NET para seus projetos caseiros","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Por que armazenar todos os dados na mem\u00f3ria?<\/h2>\n<p><\/p>\n<p>Para armazenar os dados do site ou do backend, a primeira escolha da maioria das pessoas sensatas seria um banco de dados SQL.\u00a0<\/p>\n<p><\/p>\n<p>Mas \u00e0s vezes vem \u00e0 cabe\u00e7a que o modelo de dados n\u00e3o se encaixa no SQL: por exemplo, ao construir uma busca ou um gr\u00e1fico social, \u00e9 necess\u00e1rio buscar por rela\u00e7\u00f5es complexas entre os objetos.\u00a0<\/p>\n<p><\/p>\n<p><strong>O pior \u00e9 quando voc\u00ea trabalha em equipe e um colega n\u00e3o sabe construir consultas r\u00e1pidas. Quanto tempo voc\u00ea gastou resolvendo problemas de N+1 e na cria\u00e7\u00e3o de \u00edndices adicionais para que o SELECT na p\u00e1gina principal fosse executado em um tempo razo\u00e1vel?<\/strong><\/p>\n<p><\/p>\n<p>Outra abordagem popular \u00e9 o NoSQL. Alguns anos atr\u00e1s, havia uma grande expectativa em torno desse tema \u2014 para qualquer situa\u00e7\u00e3o conveniente, era comum implementar o MongoDB e ficar feliz com as respostas no formato de documentos JSON. <em>(ali\u00e1s, quantos gambiarras voc\u00ea teve que fazer por causa de refer\u00eancias c\u00edclicas nos documentos?)<\/em>.<\/p>\n<p><\/p>\n<p>Eu proponho tentar mais uma abordagem alternativa \u2014 por que n\u00e3o tentar armazenar todos os dados na mem\u00f3ria do aplicativo, salvando periodicamente em um armazenamento arbitr\u00e1rio (arquivo, banco de dados remoto)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>A mem\u00f3ria ficou barata, e a maioria dos dados de pequenos e m\u00e9dios projetos caber\u00e1 em 1 GB de mem\u00f3ria. <em>(Por exemplo, meu projeto dom\u00e9stico favorito \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">um rastreador financeiro<\/a><\/noindex>, que mant\u00e9m estat\u00edsticas di\u00e1rias e o hist\u00f3rico de meus gastos, saldos e transa\u00e7\u00f5es ao longo de um ano e meio, consome apenas 45 MB de mem\u00f3ria.)<\/em><\/p>\n<p><\/p>\n<p>Acesso direto aos sistemas de cabos transpac\u00edficos. Ping m\u00ednimo para a China, Jap\u00e3o, Austr\u00e1lia e costa oeste dos EUA.<\/p>\n<p><\/p>\n<ul>\n<li>O acesso aos dados se torna mais f\u00e1cil \u2014 n\u00e3o precisamos nos preocupar com consultas, carregamento pregui\u00e7oso, peculiaridades da ORM, o trabalho \u00e9 feito com objetos C# normais;<\/li>\n<li>N\u00e3o h\u00e1 problemas relacionados ao acesso de diferentes threads;<\/li>\n<li>\u00c9 muito r\u00e1pido \u2014 n\u00e3o h\u00e1 solicita\u00e7\u00f5es de rede, aus\u00eancia de tradu\u00e7\u00e3o de c\u00f3digo em linguagem de consulta, n\u00e3o \u00e9 necess\u00e1rio (de)serializar objetos;<\/li>\n<li>\u00c9 aceit\u00e1vel armazenar dados em qualquer formato \u2014 seja em XML no disco, seja no SQL Server, seja no Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Pontos negativos:<\/p>\n<p><\/p>\n<ul>\n<li>A escalabilidade horizontal \u00e9 perdida, e, como consequ\u00eancia, n\u00e3o \u00e9 poss\u00edvel realizar uma implanta\u00e7\u00e3o com zero tempo de inatividade;<\/li>\n<li>Se o aplicativo falhar \u2014 alguns dados podem ser perdidos. <em>(Mas o nosso aplicativo nunca falha, certo?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Como funciona?<\/h2>\n<p><\/p>\n<p>O algoritmo \u00e9 o seguinte:<\/p>\n<p><\/p>\n<ul>\n<li>No in\u00edcio, uma conex\u00e3o \u00e9 estabelecida com o armazenamento de dados, e os dados s\u00e3o carregados;<\/li>\n<li>Uma modelo de objeto \u00e9 constru\u00eddo, \u00edndices prim\u00e1rios e \u00edndices de relacionamento (1:1, 1:Many) s\u00e3o criados;<\/li>\n<li>Uma assinatura \u00e9 criada para altera\u00e7\u00f5es nas propriedades dos objetos (INotifyPropertyChanged) e para a adi\u00e7\u00e3o ou remo\u00e7\u00e3o de elementos em uma cole\u00e7\u00e3o (INotifyCollectionChanged);<\/li>\n<li>Quando a assinatura \u00e9 acionada, o objeto modificado \u00e9 adicionado \u00e0 fila para grava\u00e7\u00e3o no armazenamento de dados;<\/li>\n<li>Periodicamente (por meio de um timer), as altera\u00e7\u00f5es s\u00e3o salvas no armazenamento em um processo de fundo;<\/li>\n<li>Ao sair do aplicativo, as altera\u00e7\u00f5es tamb\u00e9m s\u00e3o salvas no armazenamento.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Exemplo de c\u00f3digo<\/h2>\n<p>\n<b class=\"spoiler_title\">Adicionamos as depend\u00eancias necess\u00e1rias<\/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\">Descrevemos o modelo de dados que ser\u00e1 salvo no armazenamento<\/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\">Em seguida, o modelo de objeto:<\/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    \/\/ Exemplo de rela\u00e7\u00e3o 1:Many\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    \/\/ Acesso com busca por \u00edndice\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 finalmente a pr\u00f3pria classe reposit\u00f3rio para acesso aos dados:<\/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; \/\/ Para testes, permite n\u00e3o salvar altera\u00e7\u00f5es no banco\n    \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n    \n        \/\/ Se o Hangfire for utilizado e for necess\u00e1rio armazenar o modelo de dados para o Hangfire no ObjectRepository\n        \/\/ this.RegisterHangfireScheme();  \n    \n        Initialize();\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Criamos uma inst\u00e2ncia de 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 o projeto utilizar 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>Inserindo um novo objeto:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Com essa chamada, o objeto <strong>ParentModel<\/strong> \u00e9 adicionado tanto ao cache local quanto \u00e0 fila de grava\u00e7\u00e3o no banco de dados. Portanto, essa opera\u00e7\u00e3o leva O(1), e esse objeto pode ser trabalhado imediatamente.<\/p>\n<p><\/p>\n<p>Por exemplo, para encontrar esse objeto no reposit\u00f3rio e garantir que o objeto retornado seja a mesma inst\u00e2ncia:<\/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>O que acontece nessa situa\u00e7\u00e3o? <em>Set()<\/em> fundos! E ainda recebe elogios de 75-90% dos participantes, embora o desenvolvimento do projeto claramente tenha desacelerado. O mais surpreendente \u00e9 que todos foram <em>TableDictionary<\/em>, que cont\u00e9m dentro de si <em>ConcurrentDictionary<\/em> e oferece funcionalidades adicionais de \u00edndices prim\u00e1rios e secund\u00e1rios. Isso permite ter m\u00e9todos para busca por Id (ou outros \u00edndices personalizados) sem percorrer todos os objetos.<\/p>\n<p><\/p>\n<p>Ao adicionar objetos ao <em>ObjectRepository<\/em> uma assinatura para a modifica\u00e7\u00e3o de suas propriedades \u00e9 adicionada, portanto, qualquer modifica\u00e7\u00e3o nas propriedades tamb\u00e9m resulta na adi\u00e7\u00e3o desse objeto \u00e0 fila de grava\u00e7\u00e3o.\u00a0<br \/>\nA atualiza\u00e7\u00e3o das propriedades externamente se parece exatamente com o trabalho com um objeto POCO:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Updated value\";<\/code><\/pre>\n<p><\/p>\n<p>Um objeto pode ser removido das seguintes maneiras:<\/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>Nesse caso, tamb\u00e9m acontece a adi\u00e7\u00e3o do objeto \u00e0 fila de exclus\u00e3o.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Como funciona a grava\u00e7\u00e3o?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> a altera\u00e7\u00e3o dos objetos rastreados (tanto a adi\u00e7\u00e3o ou remo\u00e7\u00e3o quanto a altera\u00e7\u00e3o de propriedades) acionam o evento <em>ModelChanged<\/em>, para o qual est\u00e1 inscrito <em>IStorage<\/em>. As implementa\u00e7\u00f5es <em>IStorage<\/em> ao ocorrer o evento <em>ModelChanged<\/em> acumulam as mudan\u00e7as em 3 filas \u2014 para adi\u00e7\u00e3o, para atualiza\u00e7\u00e3o e para remo\u00e7\u00e3o.<\/p>\n<p><\/p>\n<p>Al\u00e9m disso, as implementa\u00e7\u00f5es <em>IStorage<\/em> na inicializa\u00e7\u00e3o criam um temporizador, que a cada 5 segundos chama a grava\u00e7\u00e3o das mudan\u00e7as.\u00a0<\/p>\n<p><\/p>\n<p><em>Al\u00e9m disso, existe uma API para chamada for\u00e7ada de grava\u00e7\u00e3o: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Antes de cada grava\u00e7\u00e3o, primeiro ocorre a remo\u00e7\u00e3o das opera\u00e7\u00f5es desnecess\u00e1rias das filas (como duplicatas de eventos \u2014 quando o objeto foi alterado duas vezes ou adiciona\u00e7\u00f5es\/remover r\u00e1pidas de objetos), e apenas depois a pr\u00f3pria grava\u00e7\u00e3o.\u00a0<\/p>\n<p><\/p>\n<p><em>Em todos os casos, o objeto atual \u00e9 salvo completamente, portanto, pode haver uma situa\u00e7\u00e3o em que os objetos s\u00e3o salvos em uma ordem diferente da que foram alterados, incluindo vers\u00f5es mais novas dos objetos podendo ser salvas em um momento posterior \u00e0 adi\u00e7\u00e3o na fila.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">O que mais h\u00e1?<\/h2>\n<p><\/p>\n<ul>\n<li>Todas as bibliotecas s\u00e3o baseadas no .NET Standard 2.0. Podem ser usadas em qualquer projeto moderno do .NET.<\/li>\n<li>A API \u00e9 thread-safe. As cole\u00e7\u00f5es internas s\u00e3o implementadas com base em <em>ConcurrentDictionary<\/em>, os manipuladores de eventos t\u00eam bloqueios ou n\u00e3o precisam deles.\u00a0<br \/>\nA \u00fanica coisa a lembrar \u00e9 que, ao encerrar a aplica\u00e7\u00e3o, deve-se chamar <em>ObjectRepository.Save();<\/em><\/li>\n<li>\u00cdndices arbitr\u00e1rios (exigem unicidade):<\/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\">Quem usa isso?<\/h2>\n<p><\/p>\n<p>Pessoalmente, comecei a usar essa abordagem em todos os meus projetos de hobby, porque \u00e9 conveniente e n\u00e3o requer grandes gastos para escrever uma camada de acesso a dados ou implantar uma infraestrutura pesada. Para mim, geralmente \u00e9 suficiente armazenar dados no litedb ou em um arquivo.\u00a0<\/p>\n<p><\/p>\n<p>Mas no passado, quando com a equipe fizemos a agora extinta startup EscapeTeams (<em>pensei que era isso, o dinheiro \u2014 mas n\u00e3o, foi s\u00f3 mais uma experi\u00eancia<\/em>) \u2014 usamos o Azure Table Storage para armazenar dados.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Planos futuros<\/h2>\n<p><\/p>\n<p>\u00c9 necess\u00e1rio resolver uma das principais desvantagens dessa abordagem \u2014 a escalabilidade horizontal. Para isso, s\u00e3o necess\u00e1rias transa\u00e7\u00f5es distribu\u00eddas (sic!), ou tomar uma decis\u00e3o firme de que os mesmos dados de diferentes inst\u00e2ncias n\u00e3o devem ser alterados, ou que podem ser alterados segundo o princ\u00edpio de 'quem chega por \u00faltimo, leva a melhor'.<\/p>\n<p><\/p>\n<p>Do ponto de vista t\u00e9cnico, vejo a seguinte estrutura como poss\u00edvel:<\/p>\n<p><\/p>\n<ul>\n<li>Armazenar em vez de um modelo de objeto EventLog e Snapshot<\/li>\n<li>Encontrar outras inst\u00e2ncias (adicionar nos par\u00e2metros as endpoints de todas as inst\u00e2ncias? descoberta UDP? mestre\/escravo?)<\/li>\n<li>Replicar entre inst\u00e2ncias EventLog por meio de qualquer um dos algoritmos de consenso, como RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Tamb\u00e9m existe outro problema que me preocupa \u2014 a exclus\u00e3o em cascata, ou a detec\u00e7\u00e3o de casos de exclus\u00e3o de objetos que t\u00eam refer\u00eancias de outros objetos.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">C\u00f3digo fonte<\/h2>\n<p><\/p>\n<p>Se voc\u00ea leu at\u00e9 aqui \u2014 ent\u00e3o agora s\u00f3 falta ler o c\u00f3digo, que voc\u00ea pode encontrar no 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.3 - 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\/pt\/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.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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\/pt\/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 padr\u00e3o de reposit\u00f3rio em mem\u00f3ria .NET para seus projetos caseiros | ProHoster","description":"Por que armazenar todos os dados na mem\u00f3ria? Para armazenar dados do site ou do backend, o primeiro desejo da maioria das pessoas sensatas seria escolher um banco de dados SQL.","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}