{"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\/sv\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","title":{"rendered":"ObjectRepository \u2014 .NET minneslager-m\u00f6nster f\u00f6r dina hembaserade projekt","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h2 id=\"zachem-hranit-vse-dannye-v-pamyati\">Varf\u00f6r lagra all data i minnet?<\/h2>\n<p><\/p>\n<p>F\u00f6r att lagra webbplatsens eller backenddata skulle de flesta rationella m\u00e4nniskor f\u00f6rst v\u00e4lja en SQL-databas.\u00a0<\/p>\n<p><\/p>\n<p>Men ibland dyker tanken upp att datamodellen inte passar f\u00f6r SQL: till exempel, vid byggandet av s\u00f6kningar eller sociala n\u00e4tverk beh\u00f6vs s\u00f6kningar \u00f6ver komplexa relationer mellan objekt.\u00a0<\/p>\n<p><\/p>\n<p><strong>Det v\u00e4rsta scenariot \u00e4r n\u00e4r du arbetar i ett team och en kollega inte kan bygga snabba fr\u00e5gor. Hur mycket tid har du spenderat p\u00e5 att l\u00f6sa N+1-problem och bygga extra index f\u00f6r att SELECT p\u00e5 huvudsidan ska k\u00f6ras inom rimlig tid?<\/strong><\/p>\n<p><\/p>\n<p>En annan popul\u00e4r metod \u00e4r NoSQL. F\u00f6r n\u00e5gra \u00e5r sedan var det en stor hype kring detta \u00e4mne - f\u00f6r varje l\u00e4mplig situation satte man upp MongoDB och var glad \u00f6ver svaren i form av JSON-dokument. <em>(F\u00f6rresten, hur m\u00e5nga hack fick man stoppa in p\u00e5 grund av cykliska referenser i dokumenten?)<\/em>.<\/p>\n<p><\/p>\n<p>Jag f\u00f6resl\u00e5r att vi provar ett annat alternativ - varf\u00f6r inte lagra all data i applikationens minne, och periodiskt spara i n\u00e5got fritt lagringsutrymme (fil, fj\u00e4rrdatabas)?\u00a0<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Minne har blivit billigt, och alla m\u00f6jliga data f\u00f6r de flesta sm\u00e5 och medelstora projekt f\u00e5r plats i 1 GB minne. <em>(Till exempel, mitt favorit hemarbete - <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/BudgetTracker\">en finansiell sp\u00e5rare<\/a><\/noindex>, som h\u00e5ller daglig statistik och historia \u00f6ver mina utgifter, saldon och transaktioner under de senaste ett och ett halvt \u00e5ren, anv\u00e4nder bara 45 MB minne.)<\/em><\/p>\n<p><\/p>\n<p>F\u00f6rdelar:<\/p>\n<p><\/p>\n<ul>\n<li>\u00c5tkomst till data blir enklare - du beh\u00f6ver inte bekymra dig om fr\u00e5gor, lat inl\u00e4sning, ORM-specifika egenskaper, arbetet sker med vanliga C#-objekt;<\/li>\n<li>Inga problem relaterade till \u00e5tkomst fr\u00e5n olika tr\u00e5dar;<\/li>\n<li>Mycket snabbt - inga n\u00e4tverksf\u00f6rfr\u00e5gningar, ingen kod\u00f6vers\u00e4ttning till fr\u00e5gespr\u00e5k, ingen (de)serialisering av objekt;<\/li>\n<li>Det g\u00e5r bra att lagra data i vilket format som helst - \u00e4ven i XML p\u00e5 disken, i SQL Server eller i Azure Table Storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Nackdelar:<\/p>\n<p><\/p>\n<ul>\n<li>Det horisontella skalbarheten g\u00e5r f\u00f6rlorad och som en f\u00f6ljd kan du inte g\u00f6ra noll-downtime deployment;<\/li>\n<li>Om applikationen kraschar kan det delvis leda till dataf\u00f6rlust. <em>(Men v\u00e5r applikation kraschar ju aldrig, eller hur?)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Hur fungerar det?<\/h2>\n<p><\/p>\n<p>Algoritmen \u00e4r f\u00f6ljande:<\/p>\n<p><\/p>\n<ul>\n<li>Vid starten etableras en anslutning till lagringsutrymmet och data laddas;<\/li>\n<li>En objektmodell byggs, prim\u00e4ra index och relationsindex (1:1, 1:M\u00e5nga);<\/li>\n<li>Ett abonnemang skapas f\u00f6r att \u00f6vervaka \u00e4ndringar av objekts egenskaper (INotifyPropertyChanged) och f\u00f6r att l\u00e4gga till eller ta bort element i en samling (INotifyCollectionChanged);<\/li>\n<li>Vid utl\u00f6sning av abonnemanget l\u00e4ggs det f\u00f6r\u00e4ndrade objektet i k\u00f6n f\u00f6r att skrivas till datalagringen;<\/li>\n<li>Periodiskt (via en timer) sparas \u00e4ndringarna bakgrunder i lagringen;<\/li>\n<li>\u00c4ndringarna sparas ocks\u00e5 i lagringen n\u00e4r programmet st\u00e4ngs.<\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"primer-koda\">Kodexempel<\/h2>\n<p>\n<b class=\"spoiler_title\">Vi l\u00e4gger till n\u00f6dv\u00e4ndiga beroenden<\/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\">Vi beskriver datamodellen som ska sparas i lagringen<\/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\">D\u00e4refter objektmodellen:<\/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    \/\/ Exempel p\u00e5 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    \/\/ \u00c5tkomst med indexs\u00f6kning\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\">Och slutligen sj\u00e4lva klassrepositoryt f\u00f6r datatillg\u00e5ng:<\/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; \/\/ F\u00f6r tester, till\u00e5ter att inte spara \u00e4ndringar i databasen\n    \n        AddType((ParentEntity x) =&gt; new ParentModel(x));\n        AddType((ChildEntity x) =&gt; new ChildModel(x));\n    \n        \/\/ Om Hangfire anv\u00e4nds och datamodellen ska lagras f\u00f6r Hangfire i ObjectRepository\n        \/\/ this.RegisterHangfireScheme();\n    \n        Initialize();\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Skapa en instans av 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\">Om projektet ska anv\u00e4nda 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>Infogning av ett nytt objekt:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">var newParent = new ParentModel()\nrepository.Add(newParent);<\/code><\/pre>\n<p><\/p>\n<p>Vid detta anrop \u00e4r objektet <strong>ParentModel<\/strong> l\u00e4ggs till b\u00e5de i den lokala cachen och i k\u00f6n f\u00f6r att skriva till databasen. D\u00e4rf\u00f6r tar denna operation O(1), och man kan omedelbart arbeta med detta objekt.<\/p>\n<p><\/p>\n<p>Till exempel, f\u00f6r att hitta detta objekt i lagret och s\u00e4kerst\u00e4lla att det returnerade objektet \u00e4r samma instans:<\/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>Vad h\u00e4nder d\u00e5? <em>Set()<\/em> \u00e5terv\u00e4nder <em>TableDictionary<\/em>, som inneh\u00e5ller <em>ConcurrentDictionary<\/em> och erbjuder ytterligare funktionalitet f\u00f6r prim\u00e4ra och sekund\u00e4ra index. Detta m\u00f6jligg\u00f6r metoder f\u00f6r att s\u00f6ka efter Id (eller andra godtyckliga anv\u00e4ndardefinierade index) utan att beh\u00f6va genoms\u00f6ka alla objekt.<\/p>\n<p><\/p>\n<p>Vid till\u00e4gg av objekt till <em>ObjectRepository<\/em> l\u00e4gger man till en prenumeration p\u00e5 \u00e4ndringar av deras egenskaper, s\u00e5 alla \u00e4ndringar av egenskaper resulterar ocks\u00e5 i att detta objekt l\u00e4ggs till i k\u00f6n f\u00f6r att skrivas.\u00a0<br \/>\nUppdatering av egenskaper utifr\u00e5n ser likadant ut som arbete med POCO-objekt:<\/p>\n<p><\/p>\n<pre><code class=\"cs\">myParent.Children.First().Property = \"Uppdaterat v\u00e4rde\";<\/code><\/pre>\n<p><\/p>\n<p>Ett objekt kan tas bort p\u00e5 f\u00f6ljande s\u00e4tt:<\/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>Samtidigt l\u00e4ggs objektet \u00e4ven till i k\u00f6n f\u00f6r borttagning.<\/p>\n<p><\/p>\n<h2 id=\"kak-rabotaet-sohranenie\">Hur fungerar sparandet?<\/h2>\n<p><\/p>\n<p><em>ObjectRepository<\/em> Vid \u00e4ndring av \u00f6vervakade objekt (b\u00e5de till\u00e4gg eller borttagning, samt \u00e4ndring av egenskaper) utl\u00f6ses en h\u00e4ndelse <em>ModelChanged<\/em>, som \u00e4r prenumererad p\u00e5 <em>IStorage<\/em>. Implementeringar <em>IStorage<\/em> vid h\u00e4ndelseutl\u00f6sning <em>ModelChanged<\/em> lagrar \u00e4ndringarna i 3 k\u00f6er - f\u00f6r till\u00e4gg, f\u00f6r uppdatering och f\u00f6r borttagning.<\/p>\n<p><\/p>\n<p>Implementeringar <em>IStorage<\/em> skapar dessutom en timer vid initialisering som var 5:e sekund utl\u00f6ser sparande av \u00e4ndringar.\u00a0<\/p>\n<p><\/p>\n<p><em>Dessutom finns det ett API f\u00f6r att tvinga fram sparande: <strong>ObjectRepository.Save()<\/strong>.<\/em><\/p>\n<p><\/p>\n<p>F\u00f6re varje sparande rensas f\u00f6rst k\u00f6erna fr\u00e5n meningsl\u00f6sa operationer (till exempel dubbletter av h\u00e4ndelser - n\u00e4r objektet \u00e4ndrades tv\u00e5 g\u00e5nger eller snabb till\u00e4gg\/borttagning av objekt), och f\u00f6rst d\u00e4refter sker sj\u00e4lva sparandet.\u00a0<\/p>\n<p><\/p>\n<p><em>I alla fall sparas hela det aktuella objektet, s\u00e5 det kan h\u00e4nda att objekten sparas i en annan ordning \u00e4n de \u00e4ndrades, inklusive att nyare versioner av objekt kan sparas \u00e4n vid tidpunkten f\u00f6r till\u00e4gget till k\u00f6n.<\/em><\/p>\n<p><\/p>\n<h2 id=\"chto-est-eschyo\">Vad finns det mer?<\/h2>\n<p><\/p>\n<ul>\n<li>Alla bibliotek baseras p\u00e5 .NET Standard 2.0. Kan anv\u00e4ndas i vilket modernt .NET-projekt som helst.<\/li>\n<li>API \u00e4r tr\u00e5ds\u00e4kert. De interna samlingarna \u00e4r baserade p\u00e5 <em>ConcurrentDictionary<\/em>, h\u00e4ndelsehanterare har antingen l\u00e5s eller beh\u00f6ver inga.\u00a0<br \/>\nDet enda att komma ih\u00e5g \u00e4r att vid avslutning av applikationen anropa <em>ObjectRepository.Save();<\/em><\/li>\n<li>Belbit franser (kr\u00e4ver unika v\u00e4rden):<\/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\">Vem anv\u00e4nder detta?<\/h2>\n<p><\/p>\n<p>Personligen b\u00f6rjade jag anv\u00e4nda detta tillv\u00e4gag\u00e5ngss\u00e4tt i alla mina hobbyprojekt eftersom det \u00e4r praktiskt och inte kr\u00e4ver stora investeringar f\u00f6r att skriva ett databas\u00e5tkomstlager eller s\u00e4tta upp en tung infrastruktur. F\u00f6r mig r\u00e4cker det oftast med att lagra data i litedb eller en fil.\u00a0<\/p>\n<p><\/p>\n<p>Men tidigare, n\u00e4r jag tillsammans med teamet skapade det numera nedlagda startup-f\u00f6retaget EscapeTeams (<em>trodde att h\u00e4r \u00e4r pengarna \u2014 men nej, bara erfarenhet<\/em>) \u2014 anv\u00e4nde vi Azure Table Storage f\u00f6r datalagring.<\/p>\n<p><\/p>\n<h2 id=\"plany-na-buduschee\">Framtidsplaner<\/h2>\n<p><\/p>\n<p>Det finns en vilja att \u00e5tg\u00e4rda en av de huvudsakliga nackdelarna med denna metod \u2014 horisontell skalning. F\u00f6r detta beh\u00f6vs antingen distribuerade transaktioner (sic!), eller s\u00e5 m\u00e5ste man fatta det strategiska beslutet att samma data fr\u00e5n olika instanser inte ska f\u00f6r\u00e4ndras, eller s\u00e5 f\u00e5r de f\u00f6r\u00e4ndras enligt principen \"den som \u00e4r sist \u2014 den som har r\u00e4tt\".<\/p>\n<p><\/p>\n<p>Ur teknisk synvinkel ser jag f\u00f6ljande schema som m\u00f6jligt:<\/p>\n<p><\/p>\n<ul>\n<li>Lagras i st\u00e4llet f\u00f6r objektmodellen EventLog och Snapshot<\/li>\n<li>Hitta andra instanser (l\u00e4gga till alla instansers slutpunkter i inst\u00e4llningarna? udp-uppt\u00e4ckts? master\/slave?)<\/li>\n<li>Replikera mellan instanser EventLog via n\u00e5gon av konsensusalgoritmerna, till exempel RAFT.<\/li>\n<\/ul>\n<p><\/p>\n<p>Det finns ocks\u00e5 ett annat problem som st\u00f6r mig \u2014 det kaskaddelete, eller att uppt\u00e4cka fall d\u00e4r objekt som har referenser fr\u00e5n andra objekt tas bort.\u00a0<\/p>\n<p><\/p>\n<h2 id=\"ishodnyy-kod\">K\u00e4llkod<\/h2>\n<p><\/p>\n<p>Om du har l\u00e4st hit s\u00e5 \u00e5terst\u00e5r det bara att l\u00e4sa koden, som du kan hitta p\u00e5 GitHub:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/DiverOfDark\/ObjectRepository\">https:\/\/github.com\/DiverOfDark\/ObjectRepository<\/a><\/noindex><\/p>\n<p>K\u00e4lla: <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.4 - 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\/sv\/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.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sv_SE\" \/>\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\/sv\/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 .NET in-memory repository pattern f\u00f6r dina hemprojekt | ProHoster","description":"Varf\u00f6r lagra alla data i minnet? F\u00f6r att lagra webbplatsens eller backendens data kommer de flesta rationella m\u00e4nniskor f\u00f6rst att v\u00e4lja en SQL-databas.","canonical_url":"https:\/\/prohoster.info\/sv\/blog\/news\/objectrepository-net-in-memory-repository-pattern-dlya-vashih-domashnih-proektov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sv_SE","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\/sv\/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\/sv\/wp-json\/wp\/v2\/posts\/33839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=33839"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/33839\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=33839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=33839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=33839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}