Tools.Collections
Changes
.gitignore 11(+11 -0)
src/ManagetLinkCollection/ManagetLinkCollection.ConsoleTest/ManagetLinkCollection.ConsoleTest.csproj 60(+60 -0)
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>();
+ }
+
+ }
+}