{"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\/es\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository es un patr\u00f3n de repositorio en memoria .NET para tus proyectos personales.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">\u00bfPor qu\u00e9 almacenar todos los datos en memoria?<\/h2>\n<p><\/p>\n<p>Para almacenar los datos del sitio web o del backend, la mayor\u00eda de las personas sensatas optar\u00e1n por una base de datos SQL.\u00a0<\/p>\n<p><\/p>\n<p>Pero a veces surge la idea de que el modelo de datos no se adapta a SQL: por ejemplo, al construir un motor de b\u00fasqueda o un grafo social, se necesita buscar relaciones complejas entre objetos.\u00a0<\/p>\n<p><\/p>\n<p><strong>Lo peor es cuando trabajas en equipo y un colega no sabe hacer consultas r\u00e1pidas. \u00bfCu\u00e1nto tiempo has gastado resolviendo problemas de N+1 y construyendo \u00edndices adicionales para que el SELECT en la p\u00e1gina principal se ejecute en un tiempo razonable?<\/strong><\/p>\n<p><\/p>\n<p>Otro enfoque popular es NoSQL. Hace unos a\u00f1os, hubo un gran bombo alrededor de este tema: para cualquier caso conveniente, se desplegaba MongoDB y nos alegr\u00e1bamos con las respuestas en forma de documentos JSON. <em>(por cierto, \u00bfcu\u00e1ntos parches tuviste que insertar debido a los enlaces c\u00edclicos en los documentos?)<\/em>.<\/p>\n<p><\/p>\n<p>Propongo intentar otro enfoque alternativo: \u00bfpor qu\u00e9 no almacenar todos los datos en la memoria de la aplicaci\u00f3n, guard\u00e1ndolos peri\u00f3dicamente en un almacenamiento arbitrario (archivo, base de datos remota)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>La memoria se ha vuelto barata, y cualquier dato posible de la mayor\u00eda de los proyectos peque\u00f1os y medianos cabr\u00e1 en 1 GB de RAM. <em>(Por ejemplo, mi proyecto dom\u00e9stico favorito \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">un rastreador financiero<\/a><\/noindex>, que lleva estad\u00edsticas diarias y el historial de mis gastos, saldos y transacciones durante un a\u00f1o y medio, consume solo 45 MB de memoria.)<\/em><\/p>\n<p><\/p>\n<p>Pros:<\/p>\n<p><\/p>\n<ul>\n<li>El acceso a los datos se vuelve m\u00e1s sencillo: no es necesario preocuparse por consultas, carga perezosa, peculiaridades de ORM; se trabaja con objetos C# normales;<\/li>\n<li>No hay problemas relacionados con el acceso desde diferentes hilos;<\/li>\n<li>Es muy r\u00e1pido: no hay solicitudes de red, no hay traducci\u00f3n de c\u00f3digo al lenguaje de consultas, no se necesita (de)serializaci\u00f3n de objetos;<\/li>\n<li>Se puede almacenar datos en cualquier formato: ya sea en XML en disco, en SQL Server o en Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Desventajas:<\/p>\n<p><\/p>\n<ul>\n<li>Se pierde escalabilidad horizontal, y como consecuencia no se puede realizar una implementaci\u00f3n con cero tiempo de inactividad;<\/li>\n<li>Si la aplicaci\u00f3n falla, se pueden perder parcialmente los datos. <em>(Pero nuestra aplicaci\u00f3n nunca falla, \u00bfverdad?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">\u00bfC\u00f3mo funciona?<\/h2>\n<p><\/p>\n<p>El algoritmo es el siguiente:<\/p>\n<p><\/p>\n<ul>\n<li>Al inicio, se establece una conexi\u00f3n con el almacenamiento de datos y se cargan los datos;<\/li>\n<li>Se construye el modelo de objetos, \u00edndices primarios y los \u00edndices de relaciones (1:1, 1:Muchos);<\/li>\n<li>Se crea una suscripci\u00f3n para los cambios en las propiedades de los objetos (INotifyPropertyChanged) y para la adici\u00f3n o eliminaci\u00f3n de elementos en la colecci\u00f3n (INotifyCollectionChanged);<\/li>\n<li>Cuando se activa la suscripci\u00f3n, el objeto modificado se agrega a la cola para ser escrito en el almacenamiento de datos;<\/li>\n<li>Peri\u00f3dicamente (con un temporizador) se guardan los cambios en el almacenamiento en un hilo en segundo plano;<\/li>\n<li>Al salir de la aplicaci\u00f3n, tambi\u00e9n se guardan los cambios en el almacenamiento.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Ejemplo de c\u00f3digo<\/h2>\n<p>\n<b class=\"spoiler_title\">Agregamos las dependencias necesarias<\/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\">Describimos el modelo de datos que se guardar\u00e1 en el almacenamiento<\/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\">Luego el modelo 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    \/\/ Ejemplo de relaci\u00f3n 1: muchos\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    \/\/ Acceso con b\u00fasqueda 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\">Y finalmente la clase repositorio para acceder a los datos:<\/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 pruebas, permite no guardar cambios en la base\n    \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n    \n        \/\/ Si se utiliza Hangfire y es necesario almacenar el modelo de datos para Hangfire en ObjectRepository\n        \/\/ this.RegisterHangfireScheme();\n    \n        Initialize();\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Creamos una instancia 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\">Si en el proyecto se utilizar\u00e1 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>Inserci\u00f3n de un nuevo objeto:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Con esta llamada el objeto <strong>ParentModel<\/strong> se agrega tanto a la cach\u00e9 local como a la cola para escribir en la base de datos. Por lo tanto, esta operaci\u00f3n toma O(1), y se puede trabajar con este objeto de inmediato.<\/p>\n<p><\/p>\n<p>Por ejemplo, para encontrar este objeto en el repositorio y asegurarse de que el objeto devuelto sea la misma instancia:<\/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>\u00bfQu\u00e9 ocurre en este caso? <em>Set()<\/em> devuelve <em>TableDictionary<\/em>, que contiene dentro de s\u00ed <em>ConcurrentDictionary<\/em> y proporciona funcionalidad adicional para \u00edndices primarios y secundarios. Esto permite tener m\u00e9todos para buscar por Id (u otros \u00edndices de usuario arbitrarios) sin tener que iterar por todos los objetos.<\/p>\n<p><\/p>\n<p>Al agregar objetos a <em>ObjectRepository<\/em> se a\u00f1ade una suscripci\u00f3n para el cambio de sus propiedades, por lo que cualquier cambio en las propiedades tambi\u00e9n lleva a la adici\u00f3n de este objeto a la cola para escribir.\u00a0<br \/>\nLa actualizaci\u00f3n de propiedades desde fuera se ve igual que trabajar con un objeto POCO:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Valor actualizado\";<\/code><\/pre>\n<p><\/p>\n<p>Un objeto se puede eliminar de las siguientes maneras:<\/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>Al mismo tiempo, tambi\u00e9n se a\u00f1ade el objeto a la cola de eliminaci\u00f3n.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">\u00bfC\u00f3mo funciona el guardado?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> el cambio en los objetos rastreados (tanto adiciones o eliminaciones como cambios de propiedades) provoca un evento <em>ModelChanged<\/em>, al que est\u00e1 suscrito <em>IStorage<\/em>. Las implementaciones <em>IStorage<\/em> al ocurrir el evento <em>ModelChanged<\/em> acumulan cambios en 3 colas: para adiciones, para actualizaciones y para eliminaciones.<\/p>\n<p><\/p>\n<p>Adem\u00e1s, las implementaciones <em>IStorage<\/em> al inicializar crean un temporizador que cada 5 segundos provoca el guardado de cambios.\u00a0<\/p>\n<p><\/p>\n<p><em>Adem\u00e1s, existe una API para forzar el guardado: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Antes de cada guardado, primero se eliminan de las colas las operaciones innecesarias (por ejemplo, duplicados de eventos \u2014 cuando el objeto cambi\u00f3 dos veces o se realiz\u00f3 una r\u00e1pida adici\u00f3n\/eliminaci\u00f3n de objetos), y solo despu\u00e9s se realiza el guardado en s\u00ed.\u00a0<\/p>\n<p><\/p>\n<p><em>En todos los casos, se guarda el objeto actual completo, por lo que puede haber situaciones en las que los objetos se guardan en un orden diferente al que cambiaron, incluso pueden guardarse versiones m\u00e1s nuevas de los objetos que en el momento de su adici\u00f3n a la cola.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">\u00bfQu\u00e9 m\u00e1s hay?<\/h2>\n<p><\/p>\n<ul>\n<li>Todas las bibliotecas se basan en .NET Standard 2.0. Se pueden utilizar en cualquier proyecto moderno de .NET.<\/li>\n<li>La API es segura para hilos. Las colecciones internas se implementan sobre <em>ConcurrentDictionary<\/em>, los controladores de eventos tienen bloqueos o no los necesitan.\u00a0<br \/>\nLo \u00fanico que hay que recordar es que, al finalizar la aplicaci\u00f3n, se debe llamar a <em>ObjectRepository.Save();<\/em><\/li>\n<li>\u00cdndices arbitrarios (requieren unicidad):<\/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\">\u00bfQui\u00e9n lo usa?<\/h2>\n<p><\/p>\n<p>Personalmente, comenc\u00e9 a usar este enfoque en todos mis proyectos de hobby, porque es conveniente y no requiere grandes gastos para escribir una capa de acceso a datos o desplegar una infraestructura pesada. Por lo general, para m\u00ed, es suficiente almacenar datos en litedb o en un archivo.\u00a0<\/p>\n<p><\/p>\n<p>Pero en el pasado, cuando con el equipo hicimos la startup ahora desaparecida EscapeTeams (<em>pens\u00e9 que aqu\u00ed estaba el dinero \u2014 pero no, otra vez experiencia<\/em>) \u2014 usamos Azure Table Storage para almacenar datos.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Planes futuros<\/h2>\n<p><\/p>\n<p>Es necesario abordar una de las desventajas principales de este enfoque: la escalabilidad horizontal. Para ello se requieren transacciones distribuidas (\u00a1sic!), o tomar la decisi\u00f3n de que los mismos datos de diferentes instancias no deben cambiar, o permitir que cambien bajo el principio de 'quien \u00faltimo llega, se queda con la raz\u00f3n'.<\/p>\n<p><\/p>\n<p>Desde un punto de vista t\u00e9cnico, veo posible el siguiente esquema:<\/p>\n<p><\/p>\n<ul>\n<li>Almacenar en lugar de un modelo de objetos EventLog y Snapshot<\/li>\n<li>Encontrar otras instancias (\u00bfagregar a la configuraci\u00f3n los puntos finales de todas las instancias? \u00bfdescubrimiento UDP? \u00bfmaster\/slave?)<\/li>\n<li>Replicar entre instancias EventLog a trav\u00e9s de cualquiera de los algoritmos de consenso, por ejemplo RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Tambi\u00e9n hay otro problema que me preocupa: la eliminaci\u00f3n en cascada, o la detecci\u00f3n de casos de eliminaci\u00f3n de objetos que tienen referencias de otros objetos.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">C\u00f3digo fuente<\/h2>\n<p><\/p>\n<p>Si has llegado hasta aqu\u00ed, lo \u00fanico que queda es leer el c\u00f3digo, que se puede encontrar en GitHub:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>Fuente: <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.2 - 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\/es\/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.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\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\/es\/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 patr\u00f3n de repositorio en memoria para .NET para tus proyectos dom\u00e9sticos | ProHoster","description":"\u00bfPor qu\u00e9 almacenar todos los datos en memoria? Para almacenar los datos del sitio web o del backend, el primer deseo de la mayor\u00eda de las personas con sentido com\u00fan ser\u00e1 elegir una base de datos SQL.","canonical_url":"https:\/\/prohoster.info\/es\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}