{"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\/fr\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 le mod\u00e8le de d\u00e9p\u00f4t en m\u00e9moire .NET pour vos projets maison","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Pourquoi stocker toutes les donn\u00e9es en m\u00e9moire ?<\/h2>\n<p><\/p>\n<p>Pour le stockage des donn\u00e9es d'un site ou d'un backend, la premi\u00e8re instinct de la plupart des personnes sens\u00e9es serait de choisir une base de donn\u00e9es SQL.\u00a0<\/p>\n<p><\/p>\n<p>Mais parfois, l'id\u00e9e que le mod\u00e8le de donn\u00e9es ne convient pas \u00e0 SQL vient \u00e0 l'esprit : par exemple, pour la construction d'un moteur de recherche ou d'un graphe social, il est n\u00e9cessaire de rechercher \u00e0 travers des relations complexes entre les objets.\u00a0<\/p>\n<p><\/p>\n<p><strong>La pire situation est lorsque vous travaillez en \u00e9quipe et qu'un coll\u00e8gue ne sait pas construire des requ\u00eates rapides. Combien de temps avez-vous pass\u00e9 \u00e0 r\u00e9soudre les probl\u00e8mes N+1 et \u00e0 construire des index suppl\u00e9mentaires pour que le SELECT sur la page principale s'ex\u00e9cute dans un temps raisonnable ?<\/strong><\/p>\n<p><\/p>\n<p>Une autre approche populaire est NoSQL. Il y a quelques ann\u00e9es, il y avait un grand engouement autour de ce sujet \u2014 pour toute occasion pratique, MongoDB \u00e9tait d\u00e9ploy\u00e9 et nous \u00e9tions contents des r\u00e9ponses sous forme de documents json. <em>(au fait, combien de rustines avez-vous d\u00fb mettre \u00e0 cause des r\u00e9f\u00e9rences circulaires dans les documents ?)<\/em>.<\/p>\n<p><\/p>\n<p>Je propose d'essayer une autre m\u00e9thode alternative \u2014 pourquoi ne pas essayer de stocker toutes les donn\u00e9es en m\u00e9moire de l'application, en les enregistrant p\u00e9riodiquement dans un stockage arbitraire (fichier, base de donn\u00e9es distante) ?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>La m\u00e9moire est devenue bon march\u00e9, et toutes les donn\u00e9es possibles de la plupart des petits et moyens projets peuvent tenir dans 1 Go de m\u00e9moire. <em>(Par exemple, mon projet domestique pr\u00e9f\u00e9r\u00e9 \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">un tracker financier<\/a><\/noindex>, qui tient des statistiques quotidiennes et un historique de mes d\u00e9penses, soldes et transactions depuis un an et demi, consomme seulement 45 Mo de m\u00e9moire.)<\/em><\/p>\n<p><\/p>\n<p>Avantages :<\/p>\n<p><\/p>\n<ul>\n<li>L'acc\u00e8s aux donn\u00e9es devient plus simple \u2014 pas besoin de se soucier des requ\u00eates, du chargement paresseux, des particularit\u00e9s des ORM, le travail se fait avec des objets C# ordinaires ;<\/li>\n<li>Aucun probl\u00e8me li\u00e9 \u00e0 l'acc\u00e8s \u00e0 partir de diff\u00e9rents threads ;<\/li>\n<li>Tr\u00e8s rapide \u2014 il n'y a pas de requ\u00eates r\u00e9seau, pas de translation de code en langage de requ\u00eates, pas besoin de (d\u00e9)s\u00e9rialiser les objets ;<\/li>\n<li>Il est acceptable de stocker des donn\u00e9es sous n'importe quelle forme \u2014 que ce soit en XML sur disque, dans SQL Server, ou dans Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Inconv\u00e9nients :<\/p>\n<p><\/p>\n<ul>\n<li>On perd l'\u00e9volutivit\u00e9 horizontale, et par cons\u00e9quent, il n'est pas possible de faire un d\u00e9ploiement sans arr\u00eat ;<\/li>\n<li>Si l'application \u00e9choue \u2014 il est possible de perdre partiellement des donn\u00e9es. <em>(Mais apr\u00e8s tout, notre application ne plante jamais, n'est-ce pas ?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Comment cela fonctionne ?<\/h2>\n<p><\/p>\n<p>L'algorithme est le suivant :<\/p>\n<p><\/p>\n<ul>\n<li>Au d\u00e9marrage, une connexion est \u00e9tablie avec le stockage de donn\u00e9es, et les donn\u00e9es sont charg\u00e9es ;<\/li>\n<li>Un mod\u00e8le objet est construit, des index primaires, et des index de relations (1:1, 1:Many) sont cr\u00e9\u00e9s ;<\/li>\n<li>Un abonnement est cr\u00e9\u00e9 pour les modifications des propri\u00e9t\u00e9s des objets (INotifyPropertyChanged) et pour l'ajout ou la suppression d'\u00e9l\u00e9ments dans la collection (INotifyCollectionChanged);<\/li>\n<li>Lorsqu'un abonnement est d\u00e9clench\u00e9, l'objet modifi\u00e9 est ajout\u00e9 \u00e0 la file d'attente pour \u00eatre enregistr\u00e9 dans le stockage de donn\u00e9es;<\/li>\n<li>P\u00e9riodiquement (selon un minuteur), les modifications sont sauvegard\u00e9es dans le stockage en arri\u00e8re-plan;<\/li>\n<li>Lorsque vous quittez l'application, les modifications sont \u00e9galement sauvegard\u00e9es dans le stockage.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Exemple de code<\/h2>\n<p>\n<b class=\"spoiler_title\">Ajoutons les d\u00e9pendances n\u00e9cessaires<\/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\">D\u00e9crivons le mod\u00e8le de donn\u00e9es qui sera sauvegard\u00e9 dans le stockage<\/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\">Ensuite, le mod\u00e8le d'objet :<\/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    \/\/ Exemple de relation 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    \/\/ Acc\u00e8s avec recherche par index\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\">Et enfin, la classe de r\u00e9f\u00e9rentiel pour l'acc\u00e8s aux donn\u00e9es :<\/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; \/\/ Pour les tests, permet de ne pas sauvegarder les modifications dans la base\n    \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n    \n        \/\/ Si Hangfire est utilis\u00e9 et qu'il est n\u00e9cessaire de sauvegarder le mod\u00e8le de donn\u00e9es pour Hangfire dans ObjectRepository\n        \/\/ this.RegisterHangfireScheme();\n    \n        Initialize();\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Cr\u00e9ons une instance d'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 HangFire est utilis\u00e9 dans le projet<\/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>Insertion d'un nouvel objet :<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>En appelant cela, l'objet <strong>ParentModel<\/strong> Il est ajout\u00e9 \u00e0 la fois dans le cache local et dans la file d'attente pour l'\u00e9criture dans la base de donn\u00e9es. Par cons\u00e9quent, cette op\u00e9ration prend O(1), et vous pouvez imm\u00e9diatement travailler avec cet objet.<\/p>\n<p><\/p>\n<p>Par exemple, pour trouver cet objet dans le d\u00e9p\u00f4t et s'assurer que l'objet retourn\u00e9 est le m\u00eame exemplaire :<\/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>Que se passe-t-il alors ? <em>Set()<\/em> retourne <em>TableDictionary<\/em>, qui contient <em>ConcurrentDictionary<\/em> et fournit des fonctionnalit\u00e9s suppl\u00e9mentaires pour les index primaires et secondaires. Cela permet d'avoir des m\u00e9thodes pour rechercher par Id (ou d'autres index utilisateur arbitraires) sans avoir \u00e0 parcourir tous les objets.<\/p>\n<p><\/p>\n<p>Lors de l'ajout d'objets \u00e0 <em>ObjectRepository<\/em> , un abonnement est ajout\u00e9 pour suivre les modifications de leurs propri\u00e9t\u00e9s, de sorte que toute modification des propri\u00e9t\u00e9s entra\u00eene \u00e9galement l'ajout de cet objet dans la file d'attente pour l'\u00e9criture.\u00a0<br \/>\nLa mise \u00e0 jour des propri\u00e9t\u00e9s de l'ext\u00e9rieur se fait de la m\u00eame mani\u00e8re que le travail avec un objet POCO :<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Valeur mise \u00e0 jour\";<\/code><\/pre>\n<p><\/p>\n<p>Vous pouvez supprimer un objet de plusieurs mani\u00e8res :<\/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>Cela ajoute \u00e9galement l'objet \u00e0 la file d'attente pour la suppression.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Comment fonctionne la sauvegarde ?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> la modification des objets suivis (comme l'ajout ou la suppression, ainsi que la modification des propri\u00e9t\u00e9s) d\u00e9clenche un \u00e9v\u00e9nement <em>ModelChanged<\/em>, auquel est abonn\u00e9 <em>IStorage<\/em>. Les impl\u00e9mentations <em>IStorage<\/em> lors de la survenance de l'\u00e9v\u00e9nement <em>ModelChanged<\/em> placent les modifications dans 3 files d'attente \u2014 pour l'ajout, la mise \u00e0 jour et la suppression.<\/p>\n<p><\/p>\n<p>Les impl\u00e9mentations <em>IStorage<\/em> au moment de l'initialisation cr\u00e9ent \u00e9galement un minuteur qui appelle la sauvegarde des modifications toutes les 5 secondes.\u00a0<\/p>\n<p><\/p>\n<p><em>De plus, il existe une API pour forcer l'appel de la sauvegarde : <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>Avant chaque sauvegarde, on supprime d'abord des files d'attente les op\u00e9rations inutiles (comme les \u00e9v\u00e9nements dupliqu\u00e9s \u2014 lorsque l'objet a \u00e9t\u00e9 modifi\u00e9 deux fois ou lors d'ajouts\/suppressions rapides d'objets), puis la sauvegarde proprement dite.\u00a0<\/p>\n<p><\/p>\n<p><em>Dans tous les cas, l'objet actuel est sauvegard\u00e9 int\u00e9gralement, donc il est possible que les objets soient sauvegard\u00e9s dans un ordre diff\u00e9rent de celui dans lequel ils ont \u00e9t\u00e9 modifi\u00e9s, y compris des versions plus r\u00e9centes d'objets, par rapport au moment o\u00f9 ils ont \u00e9t\u00e9 ajout\u00e9s \u00e0 la file d'attente.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Qu'y a-t-il d'autre ?<\/h2>\n<p><\/p>\n<ul>\n<li>Toutes les biblioth\u00e8ques sont bas\u00e9es sur .NET Standard 2.0. Elles peuvent \u00eatre utilis\u00e9es dans n'importe quel projet .NET moderne.<\/li>\n<li>L'API est thread-safe. Les collections internes sont bas\u00e9es sur <em>ConcurrentDictionary<\/em>, les gestionnaires d'\u00e9v\u00e9nements utilisent soit des verrouillages, soit n'en ont pas besoin.\u00a0<br \/>\nUne chose \u00e0 garder \u00e0 l'esprit est qu'il faut appeler <em>ObjectRepository.Save();<\/em><\/li>\n<li>Indexes arbitraires (n\u00e9cessitent l'unicit\u00e9) :<\/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\">Qui utilise cela ?<\/h2>\n<p><\/p>\n<p>Personnellement, j'ai commenc\u00e9 \u00e0 utiliser cette approche dans tous mes projets personnels, car c'est pratique et ne n\u00e9cessite pas de d\u00e9penses importantes pour \u00e9crire une couche d'acc\u00e8s aux donn\u00e9es ou d\u00e9ployer une infrastructure lourde. Pour moi, il suffit g\u00e9n\u00e9ralement de stocker les donn\u00e9es dans litedb ou dans un fichier.\u00a0<\/p>\n<p><\/p>\n<p>Mais dans le pass\u00e9, lorsque mon \u00e9quipe a cr\u00e9\u00e9 la startup aujourd'hui disparue EscapeTeams (<em>je pensais que l'argent \u00e9tait l\u00e0 \u2014 mais non, juste de l'exp\u00e9rience<\/em>) \u2014 nous avons utilis\u00e9 Azure Table Storage pour stocker les donn\u00e9es.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Plans pour l'avenir<\/h2>\n<p><\/p>\n<p>Il est n\u00e9cessaire de corriger l'un des principaux inconv\u00e9nients de cette approche : l'\u00e9volutivit\u00e9 horizontale. Pour cela, il faut soit des transactions distribu\u00e9es (sic !), soit prendre une d\u00e9cision ferme selon laquelle les m\u00eames donn\u00e9es provenant de diff\u00e9rentes instances ne doivent pas changer, soit les laisser changer selon le principe \u00ab le dernier \u00e0 \u00e9crire a raison \u00bb.<\/p>\n<p><\/p>\n<p>Sur le plan technique, je vois le sch\u00e9ma suivant comme possible :<\/p>\n<p><\/p>\n<ul>\n<li>Stocker au lieu du mod\u00e8le d'objet EventLog et Snapshot<\/li>\n<li>Trouver d'autres instances (ajouter dans les param\u00e8tres les points de terminaison de toutes les instances ? d\u00e9couverte UDP ? ma\u00eetre\/esclave ?)<\/li>\n<li>R\u00e9pliquer entre instances l'EventLog via n'importe quel algorithme de consensus, par exemple RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Il y a aussi un autre probl\u00e8me qui me pr\u00e9occupe \u2014 la suppression en cascade, ou la d\u00e9tection des cas de suppression d'objets auxquels d'autres objets font r\u00e9f\u00e9rence.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">Code source<\/h2>\n<p><\/p>\n<p>Si vous \u00eates arriv\u00e9 jusqu'ici \u2014 il ne reste plus qu'\u00e0 lire le code, que vous pouvez trouver sur GitHub :<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>Source : <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\/fr\/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=\"fr_FR\" \/>\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\/fr\/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 mod\u00e8le de r\u00e9f\u00e9rentiel en m\u00e9moire .NET pour vos projets personnels | ProHoster","description":"Pourquoi stocker toutes les donn\u00e9es en m\u00e9moire ? Pour le stockage des donn\u00e9es d'un site ou d'un backend, le premier souhait de la plupart des personnes sens\u00e9es serait de choisir une base de donn\u00e9es SQL.","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"fr_FR","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\/fr\/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\/fr\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}