Initial implementation

This commit is contained in:
Simon Gruber
2023-08-10 09:04:36 +02:00
commit 4c4ed48e38
95 changed files with 4142 additions and 0 deletions
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<IncludeSymbols>True</IncludeSymbols>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Lookup.Interface\Lookup.Interface.csproj" />
</ItemGroup>
</Project>
+139
View File
@@ -0,0 +1,139 @@
using Lookup.Interface;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Lookup.Abstract;
/// <summary>
/// Basic <see cref="ILookup{TKey,TValue}"/> implementation
/// </summary>
/// <typeparam name="TKey">Type of the key referencing the <typeparamref name="TValue"/>s</typeparam>
/// <typeparam name="TValue">Type of the stored values, referenced by <typeparamref name="TKey"/></typeparam>
public abstract class Lookup<TKey, TValue>
: Dictionary<TKey, ICollection<TValue>>,
ILookup<TKey, TValue>
where TKey : notnull
{
#region Constructor
/// <inheritdoc />
protected Lookup()
{
}
/// <inheritdoc />
protected Lookup(IDictionary<TKey, ICollection<TValue>> dictionary) : base(dictionary)
{
}
/// <inheritdoc />
protected Lookup(
IDictionary<TKey, ICollection<TValue>> dictionary, IEqualityComparer<TKey>? comparer
) : base(dictionary, comparer)
{
}
/// <inheritdoc />
protected Lookup(IEnumerable<KeyValuePair<TKey, ICollection<TValue>>> collection) :
base(collection)
{
}
/// <inheritdoc />
protected Lookup(
IEnumerable<KeyValuePair<TKey, ICollection<TValue>>> collection,
IEqualityComparer<TKey>? comparer
) : base(collection, comparer)
{
}
/// <inheritdoc />
protected Lookup(IEqualityComparer<TKey>? comparer) : base(comparer)
{
}
/// <inheritdoc />
protected Lookup(int capacity) : base(capacity)
{
}
/// <inheritdoc />
protected Lookup(int capacity, IEqualityComparer<TKey>? comparer) : base(capacity, comparer)
{
}
/// <inheritdoc />
protected Lookup(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endregion
#region Implementation of IDictionary<TKey,ICollection<TValue>>
/// <inheritdoc />
public void Add(TKey key, TValue value)
{
var collection = TryGetValue(key, out var values) ? values : Add(key);
collection.Add(value);
}
/// <inheritdoc />
public void AddRange(TKey key, IEnumerable<TValue> values)
{
var collection = TryGetValue(key, out var existingCollection)
? existingCollection
: Add(key);
foreach (var value in values)
{
collection.Add(value);
}
}
/// <inheritdoc />
public bool Remove(TKey key, TValue value)
{
if (!TryGetValue(key, out var values))
{
return false;
}
values.Remove(value);
return true;
}
/// <inheritdoc />
public ICollection<TValue> GetOrAdd(TKey key)
{
return TryGetValue(key, out var values)
? values
: Add(key);
}
#endregion
#region Implementation of ILookup<TKey,TValue>
/// <inheritdoc />
public abstract ICollection<TValue> Add(TKey key);
#endregion
#region Implementation of IDisposable
/// <inheritdoc cref="Dispose()"/>
protected virtual void Dispose(bool disposing)
{
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
+4
View File
@@ -0,0 +1,4 @@
# Ocr.Lookup
Base project for looking up data with different storage backends
@@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Lookup.Database.Configurations;
internal class LookupConfiguration<TKey, TValue>
: IEntityTypeConfiguration<IKeyEntity<TKey, TValue>>
where TKey : class, IKeyEntity<TKey, TValue>
where TValue : class, IValueEntity<TKey, TValue>
{
/// <inheritdoc />
public void Configure(EntityTypeBuilder<IKeyEntity<TKey, TValue>> builder)
{
builder
.HasMany(e => e.Values)
.WithMany(e => e.Keys);
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace Lookup.Database;
/// <summary>
/// Common interface for database entities
/// </summary>
public interface IEntity
{
/// <summary>
/// Id of the database entity
/// </summary>
int Id { get; set; }
}
+17
View File
@@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
namespace Lookup.Database;
/// <summary>
/// Common interface for <see cref="IEntity"/> variants
/// which reference <see cref="IValueEntity{TKey,TValue}"/> instances
/// </summary>
public interface IKeyEntity<TKey, TValue> : IEntity
where TKey : class, IKeyEntity<TKey, TValue>
where TValue : class, IValueEntity<TKey, TValue>
{
/// <summary>
/// The referenced <see cref="IValueEntity{TKey,TValue}"/> instances
/// </summary>
DbSet<IValueEntity<TKey, TValue>> Values { get; }
}
+17
View File
@@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
namespace Lookup.Database;
/// <summary>
/// Common interface for <see cref="IEntity"/> variants
/// which are referenced by <see cref="IKeyEntity{TKey,TValue}"/> instances
/// </summary>
public interface IValueEntity<TKey, TValue> : IEntity
where TKey : class, IKeyEntity<TKey, TValue>
where TValue : class, IValueEntity<TKey, TValue>
{
/// <summary>
/// The referencing <see cref="IKeyEntity{TKey,TValue}"/> instances
/// </summary>
DbSet<IKeyEntity<TKey, TValue>> Keys { get; }
}
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<IncludeSymbols>True</IncludeSymbols>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lookup.Interface\Lookup.Interface.csproj" />
</ItemGroup>
</Project>
+32
View File
@@ -0,0 +1,32 @@
using Lookup.Database.Configurations;
using Lookup.Interface;
using Microsoft.EntityFrameworkCore;
namespace Lookup.Database
{
/// <summary>
/// Basic <see cref="DbContext"/>
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public class LookupDbContext<TKey, TValue> : DbContext
where TKey : class, IKeyEntity<TKey, TValue>
where TValue : class, IRelated<TKey>, IValueEntity<TKey, TValue>
{
/// <summary>
/// Stored <see cref="IKeyEntity{TKey,TValue}"/> instances
/// </summary>
public virtual DbSet<IKeyEntity<TKey, TValue>> Keys { get; set; } = null!;
/// <summary>
/// Stored <see cref="IValueEntity{TKey,TValue}"/> instances
/// </summary>
public virtual DbSet<IValueEntity<TKey, TValue>> Values { get; set; } = null!;
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new LookupConfiguration<TKey, TValue>());
}
}
}
+4
View File
@@ -0,0 +1,4 @@
# Ocr.Lookup.Database
Database backend for lookup data storage
+40
View File
@@ -0,0 +1,40 @@
using Lookup.Memory;
namespace Lookup.IO
{
/// <summary>
/// <see cref="Interface.ILookup{TKey,TValue}"/>
/// implementation, which stores data in <see cref="ICollection{T}"/>s.
/// Supports writing the stored data to a file.
/// </summary>
/// <typeparam name="TKey">Type of the key referencing the <typeparamref name="TValue"/>s</typeparam>
/// <typeparam name="TValue">Type of the stored values, referenced by <typeparamref name="TKey"/></typeparam>
public class FileLookup<TKey, TValue> : MemoryLookup<TKey, TValue>
{
public string Path { get; set; }
public FileLookup(string path)
{
Path = path;
}
public void Save()
{
using var writer = new StreamWriter(System.IO.File.Create(Path));
foreach (var kv in this)
{
writer.WriteLine($"{kv.Key};{string.Join(',', kv.Value)}");
}
}
public new void Clear()
{
base.Clear();
if (System.IO.File.Exists(Path))
{
System.IO.File.Delete(Path);
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Lookup.Memory\Lookup.Memory.csproj" />
</ItemGroup>
</Project>
+6
View File
@@ -0,0 +1,6 @@
namespace Lookup.Interface;
public interface IKey<TKey>
{
public TKey Key { get; set; }
}
+72
View File
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace Lookup.Interface
{
/// <summary>
/// Common interface for <see cref="ILookup"/>s,
/// storing data as key-value pairs
/// </summary>
public interface ILookup
{
}
/// <summary>
/// Common interface for <see cref="ILookup{TKey, TValue}"/>s,
/// storing data as <typeparamref name="TKey"/>-<typeparamref name="TValue"/> pairs
/// </summary>
/// <typeparam name="TKey">Type of the key referencing the <typeparamref name="TValue"/>s</typeparam>
/// <typeparam name="TValue">Type of the stored values, referenced by <typeparamref name="TKey"/></typeparam>
public interface ILookup<TKey, TValue>
: ILookup,
IDictionary<TKey, ICollection<TValue>>,
IDisposable
{
/// <summary>
/// Adds a key to the <see cref="ILookup"/>
/// </summary>
/// <param name="key">
/// The <typeparamref name="TKey"/> to add
/// </param>
/// <returns>
/// The new <see cref="ICollection{TValue}"/>
/// to store <typeparamref name="TValue"/>s in
/// </returns>
ICollection<TValue> Add(TKey key);
/// <summary>
/// Adds a <typeparamref name="TValue"/> to an existing collection
/// referenced by <typeparamref name="TKey"/> in the <see cref="ILookup"/>
/// </summary>
public void Add(TKey key, TValue value);
/// <summary>
/// Adds multiple <typeparamref name="TValue"/>s to
/// an existing <see cref="ICollection{TValue}"/>
/// referenced by <typeparamref name="TKey"/> in
/// the <see cref="ILookup"/>
/// </summary>
public void AddRange(TKey key, IEnumerable<TValue> values);
/// <summary>
/// Removes a <typeparamref name="TValue"/> from
/// an existing <see cref="ICollection{TValue}"/>
/// referenced by <typeparamref name="TKey"/> in
/// the <see cref="ILookup"/>
/// </summary>
public bool Remove(TKey key, TValue value);
/// <summary>
/// Gets an existing <see cref="ICollection{TValue}"/> referenced by
/// <typeparamref name="TKey"/> or creates it, if it does not already exist
/// </summary>
/// <param name="key">
/// The <typeparamref name="TKey"/> to add
/// </param>
/// <returns>
/// The <see cref="ICollection{TValue}"/> to
/// store <typeparamref name="TValue"/>s in
/// </returns>
public ICollection<TValue> GetOrAdd(TKey key);
}
}
+8
View File
@@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Lookup.Interface;
public interface IRelated<TKey>
{
public ICollection<TKey> Keys { get; }
}
+6
View File
@@ -0,0 +1,6 @@
namespace Lookup.Interface;
public interface IValues<TValue>
{
public TValue Values { get; set; }
}
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<IncludeSymbols>True</IncludeSymbols>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
</Project>
+4
View File
@@ -0,0 +1,4 @@
# Ocr.Lookup
Base project for looking up data with different storage backends
+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<IncludeSymbols>True</IncludeSymbols>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Lookup.Abstract\Lookup.Abstract.csproj" />
</ItemGroup>
</Project>
+90
View File
@@ -0,0 +1,90 @@
using Lookup.Interface;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Lookup.Memory
{
/// <summary>
/// <see cref="ILookup{TKey,TValue}"/>
/// implementation, storing data in <see cref="ICollection{T}"/>s
/// </summary>
/// <typeparam name="TKey">Type of the key referencing the <typeparamref name="TValue"/>s</typeparam>
/// <typeparam name="TValue">Type of the stored values, referenced by <typeparamref name="TKey"/></typeparam>
public class MemoryLookup<TKey, TValue> : Abstract.Lookup<TKey, TValue>
where TKey : notnull
{
#region Constructors
/// <inheritdoc />
public MemoryLookup()
{
}
/// <inheritdoc />
public MemoryLookup(IDictionary<TKey, ICollection<TValue>> dictionary) : base(dictionary)
{
}
/// <inheritdoc />
public MemoryLookup(
IDictionary<TKey, ICollection<TValue>> dictionary, IEqualityComparer<TKey>? comparer
) : base(dictionary, comparer)
{
}
/// <inheritdoc />
public MemoryLookup(
IEnumerable<KeyValuePair<TKey, ICollection<TValue>>> collection
) : base(collection)
{
}
/// <inheritdoc />
public MemoryLookup(
IEnumerable<KeyValuePair<TKey, ICollection<TValue>>> collection,
IEqualityComparer<TKey>? comparer
) : base(collection, comparer)
{
}
/// <inheritdoc />
public MemoryLookup(IEqualityComparer<TKey>? comparer) : base(comparer)
{
}
/// <inheritdoc />
public MemoryLookup(int capacity) : base(capacity)
{
}
/// <inheritdoc />
public MemoryLookup(int capacity, IEqualityComparer<TKey>? comparer) : base(capacity, comparer)
{
}
/// <inheritdoc />
public MemoryLookup(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endregion
#region Overrides of Lookup<TKey,ICollection<TValue>>
/// <inheritdoc />
public override ICollection<TValue> Add(TKey key)
{
base.Add(key, new List<TValue>());
return this[key];
}
/// <inheritdoc cref="Add(TKey)" />
public ICollection<TValue> Add(TKey key, int collectionCapacity)
{
base.Add(key, new List<TValue>(collectionCapacity));
return this[key];
}
#endregion
}
}
@@ -0,0 +1,79 @@
using Lookup.Interface;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Lookup.Memory;
/// <summary>
/// <see cref="ILookup{TKey,TValue}"/>
/// implementation, storing weakly-referenced <typeparamref name="TValue"/>s in <see cref="ICollection{T}"/>s
/// </summary>
/// <typeparam name="TKey">Type of the key referencing the <typeparamref name="TValue"/>s</typeparam>
/// <typeparam name="TValue">Type of the stored values, referenced by <typeparamref name="TKey"/></typeparam>
public class WeakReferenceMemoryLookup<TKey, TValue>
: MemoryLookup<TKey, WeakReference<TValue>>
where TKey : notnull
where TValue : class
{
#region Constructors
/// <inheritdoc />
public WeakReferenceMemoryLookup()
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(
IDictionary<TKey, ICollection<WeakReference<TValue>>> dictionary
) : base(dictionary)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(
IDictionary<TKey, ICollection<WeakReference<TValue>>> dictionary,
IEqualityComparer<TKey>? comparer
) : base(dictionary, comparer)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(
IEnumerable<KeyValuePair<TKey, ICollection<WeakReference<TValue>>>> collection
) : base(collection)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(
IEnumerable<KeyValuePair<TKey, ICollection<WeakReference<TValue>>>> collection,
IEqualityComparer<TKey>? comparer
) : base(collection, comparer)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(IEqualityComparer<TKey>? comparer) : base(comparer)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(int capacity) : base(capacity)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(int capacity, IEqualityComparer<TKey>? comparer) : base(capacity,
comparer)
{
}
/// <inheritdoc />
public WeakReferenceMemoryLookup(SerializationInfo info, StreamingContext context) : base(info,
context)
{
}
#endregion
}
+49
View File
@@ -0,0 +1,49 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33424.131
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Memory", "Lookup.Memory\Lookup.Memory.csproj", "{330927B1-6BE3-4C89-9680-3FF6B16D26AB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.IO", "Lookup.File\Lookup.IO.csproj", "{1E08545D-6878-435E-86CE-04EE9C952860}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Database", "Lookup.Database\Lookup.Database.csproj", "{F1AA0A94-DB98-4B7D-A823-9487A396E2DD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Interface", "Lookup.Interface\Lookup.Interface.csproj", "{93B14D5B-27A9-4CB6-934E-0630CB5FE337}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lookup.Abstract", "Lookup.Abstract\Lookup.Abstract.csproj", "{49187C7B-26A4-4C02-901B-48190FC566AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{330927B1-6BE3-4C89-9680-3FF6B16D26AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{330927B1-6BE3-4C89-9680-3FF6B16D26AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{330927B1-6BE3-4C89-9680-3FF6B16D26AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{330927B1-6BE3-4C89-9680-3FF6B16D26AB}.Release|Any CPU.Build.0 = Release|Any CPU
{1E08545D-6878-435E-86CE-04EE9C952860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E08545D-6878-435E-86CE-04EE9C952860}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E08545D-6878-435E-86CE-04EE9C952860}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E08545D-6878-435E-86CE-04EE9C952860}.Release|Any CPU.Build.0 = Release|Any CPU
{F1AA0A94-DB98-4B7D-A823-9487A396E2DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1AA0A94-DB98-4B7D-A823-9487A396E2DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1AA0A94-DB98-4B7D-A823-9487A396E2DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1AA0A94-DB98-4B7D-A823-9487A396E2DD}.Release|Any CPU.Build.0 = Release|Any CPU
{93B14D5B-27A9-4CB6-934E-0630CB5FE337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{93B14D5B-27A9-4CB6-934E-0630CB5FE337}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93B14D5B-27A9-4CB6-934E-0630CB5FE337}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93B14D5B-27A9-4CB6-934E-0630CB5FE337}.Release|Any CPU.Build.0 = Release|Any CPU
{49187C7B-26A4-4C02-901B-48190FC566AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49187C7B-26A4-4C02-901B-48190FC566AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49187C7B-26A4-4C02-901B-48190FC566AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49187C7B-26A4-4C02-901B-48190FC566AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DFA659EE-FE78-4BD9-888B-78984354093E}
EndGlobalSection
EndGlobal