Tools.Collections

Details

.gitignore 11(+11 -0)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bbefd19
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+
+src/ManagetLinkCollection/.vs/ManagetLinkCollection/*
+
+
+src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/bin/*
+src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/obj/*
+
+src/ManagetLinkCollection/ManagetLinkCollection/bin/*
+src/ManagetLinkCollection/ManagetLinkCollection/obj/*
+
+src/ManagetLinkCollection/packages/*
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/App.config b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/App.config
new file mode 100644
index 0000000..56efbc7
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
+    </startup>
+</configuration>
\ No newline at end of file
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/ManagetLinkCollection.ConsoleTest.csproj b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/ManagetLinkCollection.ConsoleTest.csproj
new file mode 100644
index 0000000..81ef124
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/ManagetLinkCollection.ConsoleTest.csproj
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{54031BB4-DA24-48AB-8767-FD6850B531B1}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>ManagetLinkCollection.ConsoleTest</RootNamespace>
+    <AssemblyName>ManagetLinkCollection.ConsoleTest</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Test1.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\ManagetLinkCollection\ManagetLinkCollection.csproj">
+      <Project>{065a4afb-1ef1-467f-bd52-831359d23605}</Project>
+      <Name>ManagetLinkCollection</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Program.cs b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Program.cs
new file mode 100644
index 0000000..69cb068
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ManagetLinkCollection.ConsoleTest
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Test1 test = new Test1();
+
+            var storage = test.TestMethod1();
+            var items = storage.Storage;
+
+
+            test.TestMethod2();
+        }
+    }
+}
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Properties/AssemblyInfo.cs b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..77cef2f
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Общие сведения об этой сборке предоставляются следующим набором
+// набора атрибутов. Измените значения этих атрибутов для изменения сведений,
+// связанные с этой сборкой.
+[assembly: AssemblyTitle("ManagetLinkCollection.ConsoleTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ManagetLinkCollection.ConsoleTest")]
+[assembly: AssemblyCopyright("Copyright ©  2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми
+// для компонентов COM. Если необходимо обратиться к типу в этой сборке через
+// из модели COM задайте для атрибута ComVisible этого типа значение true.
+[assembly: ComVisible(false)]
+
+// Следующий GUID представляет идентификатор typelib, если этот проект доступен из модели COM
+[assembly: Guid("54031bb4-da24-48ab-8767-fd6850b531b1")]
+
+// Сведения о версии сборки состоят из указанных ниже четырех значений:
+//
+//      Основной номер версии
+//      Дополнительный номер версии
+//      Номер сборки
+//      Номер редакции
+//
+// Можно задать все значения или принять номера сборки и редакции по умолчанию 
+// используя "*", как показано ниже:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Test1.cs b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Test1.cs
new file mode 100644
index 0000000..dc7949a
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/Test1.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Threading;
+
+using ManagetLinkCollection.LinkStorage;
+using ManagetLinkCollection.LinkStorage.Entities;
+using ManagetLinkCollection.ObjectLinkStorage;
+
+namespace ManagetLinkCollection.ConsoleTest
+{
+    class Test1
+    {
+
+        public LinkStorage<int, int> TestMethod1()
+        {
+            LinkStorage<int, int> storage
+                = new LinkStorage<int, int>();
+
+            storage.AddLink(1, (int k) => 1);
+
+            Task.Run(() =>
+            {
+                storage.AddLink(1,
+                    (int k) =>
+                    {
+                        Write("t1", "add start");
+                        Thread.Sleep(5000);
+
+                        Write("t1", "add stop");
+                        return -1;
+                    },
+                (ILinkItem<int, int> old_v) =>
+                {
+                    Write("t1", "update start");
+                    Thread.Sleep(5000);
+
+                    Write("t1", "update stop");
+                    return -1;
+                });
+            });
+
+            Thread.Sleep(200);
+
+            Task.Run(() =>
+            {
+                storage.AddLink(1,
+                    (int k) =>
+                    {
+                        Write("t2", "add start");
+                        //Thread.Sleep(5000);
+
+                        Write("t2", "add stop");
+                        return -2;
+                    },
+                (ILinkItem<int, int> old_v) =>
+                {
+                    Write("t2", "update start");
+                    Write("t2", "update stop");
+
+                    //d.TryRemove(k, out int v);
+                    //throw new OperationCanceledException();
+
+                    return -2;
+                });
+            });
+
+            Console.ReadLine();
+            return storage;
+        }
+
+        public void TestMethod2()
+        {
+            LinkStorage<int, int> storage
+                = new LinkStorage<int, int>();
+
+            storage.AddLink(1, (int k) => 1);
+
+            Task.Run(() =>
+            {
+                storage.AddLink(1,
+                    (int k) =>
+                    {
+                        Write("t1", "add start");
+                        Thread.Sleep(5000);
+
+                        Write("t1", "add stop");
+                        return -1;
+                    },
+                (ILinkItem<int, int> old_v) =>
+                {
+                    Write("t1", "update start");
+                    Thread.Sleep(5000);
+
+                    Write("t1", "update stop");
+                    return -1;
+                });
+            });
+
+            Thread.Sleep(200);
+
+            Task.Run(() =>
+            {
+                storage.RemoveLink(1,
+                (ILinkItem<int, int> old_v) =>
+                {
+                    Write("t2", "update start");
+
+                    Write("t2", "update stop");
+                    return old_v.Data;
+                }
+                    );
+            });
+
+            Console.ReadLine();
+        }
+
+
+        static void Write(string thread, string text)
+        {
+            Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}|{thread}|{text}");
+        }
+
+    }
+}
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection.sln b/src/ManagetLinkCollection/ManagetLinkCollection.sln
new file mode 100644
index 0000000..b3d6571
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection.sln
@@ -0,0 +1,39 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30128.74
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{4E02ADEF-E2A1-4C5C-9A28-46207FBA1DF1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{5879AB39-82D6-4630-8AFE-741E0DB76D3F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagetLinkCollection.ConsoleTest", "ManagetLinkCollection.ConsoleTest\ManagetLinkCollection.ConsoleTest.csproj", "{54031BB4-DA24-48AB-8767-FD6850B531B1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagetLinkCollection", "ManagetLinkCollection\ManagetLinkCollection.csproj", "{065A4AFB-1EF1-467F-BD52-831359D23605}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{54031BB4-DA24-48AB-8767-FD6850B531B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{54031BB4-DA24-48AB-8767-FD6850B531B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{54031BB4-DA24-48AB-8767-FD6850B531B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{54031BB4-DA24-48AB-8767-FD6850B531B1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{065A4AFB-1EF1-467F-BD52-831359D23605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{065A4AFB-1EF1-467F-BD52-831359D23605}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{065A4AFB-1EF1-467F-BD52-831359D23605}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{065A4AFB-1EF1-467F-BD52-831359D23605}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{54031BB4-DA24-48AB-8767-FD6850B531B1} = {5879AB39-82D6-4630-8AFE-741E0DB76D3F}
+		{065A4AFB-1EF1-467F-BD52-831359D23605} = {4E02ADEF-E2A1-4C5C-9A28-46207FBA1DF1}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {8900C5DB-BAB5-403F-9613-9CB9F822BDD5}
+	EndGlobalSection
+EndGlobal
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/Entities/ILinkItem.cs b/src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/Entities/ILinkItem.cs
new file mode 100644
index 0000000..3aa4948
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/Entities/ILinkItem.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ManagetLinkCollection.LinkStorage.Entities
+{
+    public interface ILinkItem<TKey, TData>
+    {
+        /// <summary>
+        /// Значение ключа
+        /// </summary>
+        TKey Key { get; }
+
+        /// <summary>
+        /// Кол-во объектов, ссылающихся на данные
+        /// </summary>
+        int LinkCount { get; }
+
+        /// <summary>
+        /// Данные
+        /// </summary>
+        TData Data { get; }
+    }
+
+    internal class RealLinkItem<TKey, TData> 
+        : ILinkItem<TKey, TData>
+    {
+        /// <summary>
+        /// Используется как штамп, для понимания изменилась ли исходная сущность после начала операции.
+        /// </summary>
+        //private DateTime TimeStamp { set; get; } = DateTime.Now;
+        private Guid Stamp { set; get; } = Guid.NewGuid();
+
+
+        public TKey Key { private set; get; }
+        public int LinkCount { private set; get; }
+        public TData Data { private set; get; }
+
+
+        public RealLinkItem(TKey key, int linkCount)
+        {
+            Key = key;
+            LinkCount = linkCount;
+        }
+        public RealLinkItem(TKey key,  int linkCount, TData data)
+        {
+            Key = key;
+            LinkCount = linkCount;
+            Data = data;
+        }
+
+        public override bool Equals(object obj)
+        {
+            switch (obj)
+            {
+                case null:
+                    return false;
+
+                case RealLinkItem<TKey, TData> item:
+                    return
+                        Key.Equals(item.Key)
+                        //TimeStamp.Equals(item.TimeStamp)
+                        && Stamp.Equals(item.Stamp)
+                        && LinkCount.Equals(item.LinkCount);
+                case RemoveLinkItem<TKey, TData> item:
+                    return
+                        LinkCount <= 0;
+
+                default:
+                    return false;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Специальный объект для удаления
+    /// эквивалентен любой записи со значением ссылок <= 0
+    /// </summary>
+    internal class RemoveLinkItem<TKey, TData> : ILinkItem<TKey, TData>
+    {
+        public TKey Key => throw new NotImplementedException();
+        public int LinkCount => throw new NotImplementedException();
+        public TData Data => throw new NotImplementedException();
+
+        public override bool Equals(object obj)
+        {
+            switch (obj)
+            {
+                case ILinkItem<TKey, TData> item:
+                    return
+                        item.LinkCount <= 0;
+                default:
+                    return false;
+            }
+        }
+    }
+
+}
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/LinkStorage.cs b/src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/LinkStorage.cs
new file mode 100644
index 0000000..c0384fc
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection/LinkStorage/LinkStorage.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Collections.Concurrent;
+
+using ManagetLinkCollection.LinkStorage.Entities;
+
+namespace ManagetLinkCollection.LinkStorage
+{
+    /// <summary>
+    /// KeyValue хранилище ссылок на данные
+    /// Запись удаляется из хранилища, когда кол-во ссылок на нее становиться равным 0
+    /// </summary>
+    /// <typeparam name="TKey">Ключ</typeparam>
+    /// <typeparam name="TData">Данные</typeparam>
+    public class LinkStorage<TKey, TData>
+    {
+
+        #region
+
+        public IReadOnlyDictionary<TKey, ILinkItem<TKey, TData>> Storage { private set; get; }
+
+
+        private readonly ConcurrentDictionary<TKey, ILinkItem<TKey, TData>> _Storage
+            = new ConcurrentDictionary<TKey, ILinkItem<TKey, TData>>();
+        private readonly ICollection<KeyValuePair<TKey, ILinkItem<TKey, TData>>> _StorageAsCollection;
+
+        #endregion
+
+
+        #region
+
+        public LinkStorage()
+        {
+            Storage = (IReadOnlyDictionary<TKey, ILinkItem<TKey, TData>>)_Storage;
+            _StorageAsCollection = _Storage;
+        }
+
+        #endregion
+
+
+        #region
+
+        /// <summary>
+        /// Добавить ссылку на ключ
+        /// </summary>
+        /// <param name="key">ключ</param>
+        /// <param name="addFactory">Фабрика создания данных записи</param>
+        /// <param name="updateFactory">Фабрика обновления данных по записи</param>
+        public ILinkItem<TKey, TData> AddLink(
+            TKey key, 
+            Func<TKey, TData> addFactory, 
+            Func<ILinkItem<TKey, TData>, TData> updateFactory
+            )
+        {
+            return _Storage.AddOrUpdate(key,
+                (TKey k) =>
+                {
+                    return new RealLinkItem<TKey, TData>(
+                        key,
+                        1, 
+                        addFactory != null ? addFactory(key) : default(TData)
+                    );
+                },
+                (TKey k, ILinkItem<TKey, TData> old_value) =>
+                {
+                    return new RealLinkItem<TKey, TData>(
+                        key,
+                        old_value.LinkCount + 1, 
+                        updateFactory != null ? updateFactory(old_value) : old_value.Data
+                        );
+                }
+            );
+
+        }
+
+        public ILinkItem<TKey, TData> AddLink(
+            TKey key, 
+            Func<TKey, TData> addFactory)
+        {
+            return AddLink(key, addFactory, null);
+        }
+
+        public ILinkItem<TKey, TData> AddLink(
+            TKey key, 
+            Func<ILinkItem<TKey, TData>, TData> updateFactory)
+        {
+            return AddLink(key, null, updateFactory);
+        }
+
+        public ILinkItem<TKey, TData> AddLink(
+            TKey key
+            )
+        {
+            return AddLink(key, null, null);
+        }
+
+
+        /// <summary>
+        /// Удалить ссылку по ключу
+        /// </summary>
+        public ILinkItem<TKey, TData> RemoveLink(
+            TKey key, 
+            Func<ILinkItem<TKey, TData>, TData> updateFactory)
+        {
+            var item = _Storage.AddOrUpdate(key,
+                (TKey k) =>
+                {
+                    return new RealLinkItem<TKey, TData>(
+                        key,
+                        0, 
+                        default(TData)
+                        );
+                },
+                (TKey k, ILinkItem<TKey, TData> old_value) =>
+                {
+                    return new RealLinkItem<TKey, TData>(
+                        key,
+                        old_value.LinkCount - 1, 
+                        updateFactory != null ? updateFactory(old_value) : old_value.Data
+                        );
+                }
+            );
+
+            if (item.LinkCount == 0)
+            {
+                RemoveIfLinkIsNull(key);
+            }
+
+            return item;
+        }
+
+        public ILinkItem<TKey, TData> RemoveLink(TKey key)
+        {
+            return RemoveLink(key, null);
+        }
+
+        #endregion
+
+
+
+        private static readonly RemoveLinkItem<TKey, TData> RemoveItem
+            = new RemoveLinkItem<TKey, TData>();
+        private void RemoveIfLinkIsNull(TKey key)
+        {
+            _StorageAsCollection.Remove
+            (
+                new KeyValuePair<TKey, ILinkItem<TKey, TData>>(key, RemoveItem)
+            );
+        }
+
+    }
+}
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection/ManagetLinkCollection.csproj b/src/ManagetLinkCollection/ManagetLinkCollection/ManagetLinkCollection.csproj
new file mode 100644
index 0000000..b6b3c6f
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection/ManagetLinkCollection.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard1.1</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="System.Collections.Concurrent" Version="4.3.0" />
+    <PackageReference Include="System.Collections.Immutable" Version="1.7.1" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection/ObjectLinkStorage/Entities/ObjectLinkContainer.cs b/src/ManagetLinkCollection/ManagetLinkCollection/ObjectLinkStorage/Entities/ObjectLinkContainer.cs
new file mode 100644
index 0000000..6ff26bd
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection/ObjectLinkStorage/Entities/ObjectLinkContainer.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Collections.Immutable;
+
+namespace ManagetLinkCollection.ObjectLinkStorage.Entities
+{
+    public class ObjectLinkContainer<TData, TObject>
+    {
+        /// <summary>
+        /// Данные, доступные по ключу
+        /// </summary>
+        public TData Data { internal set; get; }
+
+        /// <summary>
+        /// Множество объектов, которые ссылаются на эти данные
+        /// </summary>
+        public ImmutableHashSet<TObject> Objects { internal set; get; }
+
+
+        internal ObjectLinkContainer() { }    
+
+    }
+
+
+    internal class ContainerBuilder<TData, TObject>
+    {
+        private ObjectLinkContainer<TData, TObject> Container;
+
+
+        public ContainerBuilder() 
+        {
+            Container = new ObjectLinkContainer<TData, TObject>();
+        }
+        public ContainerBuilder(ObjectLinkContainer<TData, TObject> container) 
+        {
+            Container = container;
+        }
+
+
+        public ContainerBuilder<TData, TObject> AddObject(TObject obj)
+        {
+            Container = new ObjectLinkContainer<TData, TObject>()
+            {
+                Objects = Container.Objects.Add(obj),
+                Data = Container.Data
+            };
+
+            return this;
+        }
+        public ContainerBuilder<TData, TObject> RemoveObject(TObject obj)
+        {
+            Container = new ObjectLinkContainer<TData, TObject>()
+            {
+                Objects = Container.Objects.Remove(obj),
+                Data = Container.Data
+            };
+
+            return this;
+        }
+        public ContainerBuilder<TData, TObject> UpdateData(TData data)
+        {
+            Container = new ObjectLinkContainer<TData, TObject>()
+            {
+                Objects = Container.Objects,
+                Data = data
+            };
+
+            return this;
+        }
+
+        public ObjectLinkContainer<TData, TObject> Result()
+        {
+            return Container;
+        }
+
+    }
+
+}
diff --git a/src/ManagetLinkCollection/ManagetLinkCollection/ObjectLinkStorage/ObjectLinkStorage.cs b/src/ManagetLinkCollection/ManagetLinkCollection/ObjectLinkStorage/ObjectLinkStorage.cs
new file mode 100644
index 0000000..f3c9b8d
--- /dev/null
+++ b/src/ManagetLinkCollection/ManagetLinkCollection/ObjectLinkStorage/ObjectLinkStorage.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using ManagetLinkCollection.LinkStorage;
+using ManagetLinkCollection.LinkStorage.Entities;
+
+using ManagetLinkCollection.ObjectLinkStorage.Entities;
+
+namespace ManagetLinkCollection.ObjectLinkStorage
+{
+
+    /// <summary>
+    /// Хранилище ссылок на данные.
+    /// Хранящее данные о множестве объектов, ссылающихся на запись.
+    /// </summary>
+    /// <typeparam name="TKey">Ключ</typeparam>
+    /// <typeparam name="TData">Данные</typeparam>
+    /// <typeparam name="TObject">Объект, ссылающийся на данные</typeparam>
+    public class ObjectLinkStorage<TKey, TData, TObject>
+        where TObject : class
+    {
+
+        #region
+
+        public IReadOnlyDictionary<TKey, ILinkItem<TKey, ObjectLinkContainer<TData, TObject>>> Storage { private set; get; }
+
+        private readonly LinkStorage<TKey, ObjectLinkContainer<TData, TObject>> _Storage
+            = new LinkStorage<TKey, ObjectLinkContainer<TData, TObject>>();
+
+        #endregion
+
+
+        #region
+
+        public ObjectLinkStorage()
+        {
+            Storage = _Storage.Storage;
+        }
+
+        #endregion
+
+
+        #region
+
+        /// <summary>
+        /// Добавить ссылку на запись
+        /// </summary>
+        /// <param name="key">Ключ</param>
+        /// <param name="value">Ссылающийся объект</param>
+        /// <param name="addFactory">Фабрика создания данных записи</param>
+        /// <param name="updateFactory">Фабрика обновления данных по ключу</param>
+        public ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> AddLink(
+            TKey key,
+            TObject value,
+            Func<TKey, TData> addFactory,
+            Func<ILinkItem<TKey, ObjectLinkContainer<TData, TObject>>, TData> updateFactory
+            )
+        {
+            return _Storage.AddLink
+                (
+                key,
+                (_key) =>
+                    CreateBuilder()
+                        .AddObject(value)
+                        .UpdateData(addFactory != null ? addFactory(_key) : default(TData))
+                        .Result(),
+                (ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> oldValue) =>
+                    CreateBuilder(oldValue.Data)
+                        .AddObject(value)
+                        .UpdateData(updateFactory != null ? updateFactory(oldValue) : oldValue.Data.Data)
+                        .Result()
+                );
+        }
+
+        public ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> AddLink(
+            TKey key,
+            TObject value,
+            Func<TKey, TData> addFactory
+            )
+        {
+            return AddLink(key, value, addFactory, null);
+        }
+
+        public ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> AddLink(
+            TKey key,
+            TObject value,
+            Func<ILinkItem<TKey, ObjectLinkContainer<TData, TObject>>, TData> updateFactory
+            )
+        {
+            return AddLink(key, value, null, updateFactory);
+        }
+
+        public ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> AddLink(
+            TKey key,
+            TObject value
+            )
+        {
+            return AddLink(key, value, null, null);
+        }
+
+
+        public ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> RemoveLink(
+            TKey key,
+            TObject value,
+            Func<ILinkItem<TKey, ObjectLinkContainer<TData, TObject>>, TData> updateFactory
+            )
+        {
+            return _Storage.RemoveLink(
+                key,
+                (ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> oldValue) =>
+                    new ContainerBuilder<TData, TObject>(oldValue.Data)
+                        .RemoveObject(value)
+                        .UpdateData(updateFactory != null ? updateFactory(oldValue) : oldValue.Data.Data)
+                        .Result()
+                );
+        }
+
+        public ILinkItem<TKey, ObjectLinkContainer<TData, TObject>> RemoveLink(
+            TKey key,
+            TObject value
+            )
+        {
+            return RemoveLink(key, value, null);
+        }
+
+        #endregion
+
+
+
+        private ContainerBuilder<TData, TObject> CreateBuilder(ObjectLinkContainer<TData, TObject> container = null) 
+        {
+            return
+                container != null
+                ? new ContainerBuilder<TData, TObject>(container)
+                : new ContainerBuilder<TData, TObject>();                
+        }
+
+    }
+}