AspNet extensions

This commit is contained in:
JFronny 2020-11-19 19:26:36 +01:00
parent bb5ea72041
commit 9d87c47f1c
8 changed files with 419 additions and 2 deletions

View File

@ -55,6 +55,11 @@ uptool:
$tmp = $(Get-Item $(Resolve-Path *.nupkg).Path).Name
echo $tmp
dotnet nuget push $tmp -s https://api.nuget.org/v3/index.json -k $NUGET
cd ..\AspNet
dotnet pack --version-suffix "$suffix" -c Release -o .
$tmp = $(Get-Item $(Resolve-Path *.nupkg).Path).Name
echo $tmp
dotnet nuget push $tmp -s https://api.nuget.org/v3/index.json -k $NUGET
cd ..\W32
dotnet pack --version-suffix "$suffix" -c Release -o .
$tmp = $(Get-Item $(Resolve-Path *.nupkg).Path).Name

View File

@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.CC-Functions/.idea/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.CC-Functions/.idea/riderModule.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.CC-Functions/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.CC-Functions/riderModule.iml" />
</modules>
</component>
</project>

32
AspNet/AspNet.csproj Normal file
View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>CC_Functions.AspNet</RootNamespace>
<AssemblyName>CC_Functions.AspNet</AssemblyName>
<Deterministic>false</Deterministic>
<PackageId>CC-Functions.AspNet</PackageId>
<Title>CC-Functions.AspNet</Title>
<Authors>JFronny</Authors>
<Description>Random pieces of code for Asp.Net, including my SerialDict integrated Database</Description>
<Copyright>Copyright 2020</Copyright>
<PackageProjectUrl>https://gitlab.com/JFronny/CC-Functions</PackageProjectUrl>
<RepositoryUrl>https://gitlab.com/JFronny/CC-Functions.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<AssemblyVersion>1.1.*</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<VersionSuffix>0.0</VersionSuffix>
<PackageVersion>1.1.$(VersionSuffix)</PackageVersion>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DocumentationFile>bin\Debug\Core.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DocumentationFile>bin\Release\Core.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="3.0.62" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace CC_Functions.AspNet
{
/// <summary>
/// Provides serializing dictionary with Guid keys
/// </summary>
public class DictionaryGuidConverter : JsonConverterFactory
{
/// <inheritdoc />
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>))
{
return false;
}
return typeToConvert.GetGenericArguments()[0] == typeof(Guid);
}
/// <inheritdoc />
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
Type valueType = typeToConvert.GetGenericArguments()[1];
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
typeof(DictionaryGuidConverterInner<>).MakeGenericType(valueType),
BindingFlags.Instance | BindingFlags.Public,
null,
new object[] { options },
null);
return converter;
}
private class DictionaryGuidConverterInner<TValue> : JsonConverter<Dictionary<Guid, TValue>>
{
private readonly JsonConverter<TValue> _valueConverter;
private Type _valueType;
public DictionaryGuidConverterInner(JsonSerializerOptions options)
{
// For performance, use the existing converter if available.
_valueConverter = (JsonConverter<TValue>)options
.GetConverter(typeof(TValue));
// Cache the key and value types.
_valueType = typeof(TValue);
}
public override Dictionary<Guid, TValue> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
Dictionary<Guid, TValue> dictionary = new();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return dictionary;
}
// Get the key.
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
string propertyName = reader.GetString();
// For performance, parse with ignoreCase:false first.
if (!Guid.TryParse(propertyName, out Guid key))
{
throw new JsonException(
$"Unable to convert \"{propertyName}\" to Guid.");
}
// Get the value.
TValue v;
if (_valueConverter != null)
{
reader.Read();
v = _valueConverter.Read(ref reader, _valueType, options);
}
else
{
v = JsonSerializer.Deserialize<TValue>(ref reader, options);
}
// Add to dictionary.
dictionary.Add(key, v);
}
throw new JsonException();
}
public override void Write(
Utf8JsonWriter writer,
Dictionary<Guid, TValue> dictionary,
JsonSerializerOptions options)
{
writer.WriteStartObject();
foreach (KeyValuePair<Guid, TValue> kvp in dictionary)
{
writer.WritePropertyName(kvp.Key.ToString());
if (_valueConverter != null)
{
_valueConverter.Write(writer, kvp.Value, options);
}
else
{
JsonSerializer.Serialize(writer, kvp.Value, options);
}
}
writer.WriteEndObject();
}
}
}
}

164
AspNet/SaveLoadDict.cs Normal file
View File

@ -0,0 +1,164 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace CC_Functions.AspNet
{
/// <summary>
/// Provides synchronizing for dictionaries with saving/loading backends
/// </summary>
/// <typeparam name="T">The key type</typeparam>
/// <typeparam name="U">The param type</typeparam>
public abstract class SaveLoadDict<T, U> : IDictionary<T, U>
{
/// <inheritdoc />
public IEnumerator<KeyValuePair<T, U>> GetEnumerator()
{
lock (this) return Load().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
lock (this) return GetEnumerator();
}
/// <inheritdoc />
public void Add(KeyValuePair<T, U> item)
{
lock (this) Add(item.Key, item.Value);
}
/// <inheritdoc />
public void Clear()
{
lock (this) Save(new Dictionary<T, U>());
}
/// <inheritdoc />
public bool Contains(KeyValuePair<T, U> item)
{
lock (this) return Load().Contains(item);
}
/// <inheritdoc />
public void CopyTo(KeyValuePair<T, U>[] array, int arrayIndex)
{
lock (this) Load().CopyTo(array, arrayIndex);
}
/// <inheritdoc />
public bool Remove(KeyValuePair<T, U> item)
{
lock (this)
{
IDictionary<T, U> dictionary = Load();
try
{
return dictionary.Remove(item);
}
finally
{
Save(dictionary);
}
}
}
/// <inheritdoc />
public int Count
{
get { lock (this) return Load().Count; }
}
/// <inheritdoc />
public bool IsReadOnly => false;
/// <inheritdoc />
public void Add(T key, U value)
{
lock (this)
{
IDictionary<T, U> dictionary = Load();
dictionary.Add(key, value);
Save(dictionary);
}
}
/// <inheritdoc />
public bool ContainsKey(T key)
{
lock (this) return Load().ContainsKey(key);
}
/// <inheritdoc />
public bool Remove(T key)
{
lock (this)
{
IDictionary<T, U> dictionary = Load();
try
{
return dictionary.Remove(key);
}
finally
{
Save(dictionary);
}
}
}
/// <inheritdoc />
public bool TryGetValue(T key, out U value)
{
lock (this) return Load().TryGetValue(key, out value);
}
/// <inheritdoc />
public U this[T key]
{
get
{
lock (this) return Load()[key];
}
set
{
lock (this)
{
IDictionary<T, U> dictionary = Load();
dictionary[key] = value;
Save(dictionary);
}
}
}
/// <inheritdoc />
public ICollection<T> Keys
{
get { lock (this) return Load().Keys; }
}
/// <inheritdoc />
public ICollection<U> Values
{
get { lock (this) return Load().Values; }
}
/// <summary>
/// Replace the current content based on the current state
/// </summary>
/// <param name="f">Function to mutate the content</param>
public void Mutate(Func<IDictionary<T, U>, IDictionary<T, U>> f)
{
lock (this) Save(f(Load()));
}
/// <summary>
/// Save the dictionary content
/// </summary>
/// <param name="v">Dictionary content to save</param>
protected abstract void Save(IDictionary<T, U> v);
/// <summary>
/// Load the content to a dictionary
/// </summary>
/// <returns>The loaded content</returns>
protected abstract IDictionary<T, U> Load();
}
}

73
AspNet/SerialDict.cs Normal file
View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ProtoBuf;
namespace CC_Functions.AspNet
{
/// <summary>
/// An implementation of SaveLoadDict that uses Protobuf-net for serialization and Guid keys
/// </summary>
/// <typeparam name="T">The data type</typeparam>
public abstract class SerialDict<T> : SaveLoadDict<Guid, T>
{
//Interface
/// <summary>
/// Gets the directory containing databases
/// </summary>
protected abstract string DatabasesDir { get; }
/// <summary>
/// Gets the file name for this database. Must not contain the path
/// </summary>
protected abstract string DatabaseFileName { get; }
/// <summary>
/// Called when the database is loaded. Use for preparing initial entries
/// </summary>
public event LoadedD Loaded;
/// <summary>
/// Called when the database is loaded. Use for preparing initial entries
/// </summary>
/// <param name="v">The current dictionary content</param>
public delegate void LoadedD(IDictionary<Guid, T> v);
/// <summary>
/// Loads the dictionary and replaces Guids with strings. Use for sending
/// </summary>
/// <returns>The simplified dictionary</returns>
public IDictionary<string, T> GetSendable() => Load().ToDictionary(s => s.Key.ToString(), s => s.Value);
//Internal
private string GetRel() => Path.Combine(DatabasesDir, DatabaseFileName);
/// <inheritdoc />
protected override IDictionary<Guid, T> Load()
{
if (!Directory.Exists(DatabasesDir))
Directory.CreateDirectory(DatabasesDir);
IDictionary<Guid, T> v;
if (File.Exists(GetRel()))
{
using (FileStream file = File.OpenRead(GetRel()))
v = Serializer.Deserialize<Dictionary<Guid, T>>(file);
Loaded?.Invoke(v);
return v;
}
else
{
v = new Dictionary<Guid, T>();
Loaded?.Invoke(v);
Save(v);
return v;
}
}
/// <inheritdoc />
protected override void Save(IDictionary<Guid, T> dict)
{
if (!Directory.Exists(DatabasesDir))
Directory.CreateDirectory(DatabasesDir);
using FileStream file = File.Create(GetRel());
Serializer.Serialize(file, dict);
}
}
}

View File

@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLITest", "CLITest\CLITest.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{780EC190-E223-46DE-B6FB-1CF56ABDF34E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet", "AspNet\AspNet.csproj", "{1A9F3CD1-559B-4429-B8A6-490AF7188A2E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -45,6 +47,10 @@ Global
{780EC190-E223-46DE-B6FB-1CF56ABDF34E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{780EC190-E223-46DE-B6FB-1CF56ABDF34E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{780EC190-E223-46DE-B6FB-1CF56ABDF34E}.Release|Any CPU.Build.0 = Release|Any CPU
{1A9F3CD1-559B-4429-B8A6-490AF7188A2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A9F3CD1-559B-4429-B8A6-490AF7188A2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A9F3CD1-559B-4429-B8A6-490AF7188A2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A9F3CD1-559B-4429-B8A6-490AF7188A2E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -4,7 +4,6 @@
<OutputType>Library</OutputType>
<RootNamespace>CC_Functions.Core</RootNamespace>
<AssemblyName>CC_Functions.Core</AssemblyName>
<LangVersion>8</LangVersion>
<Deterministic>false</Deterministic>
<PackageId>CC-Functions.Core</PackageId>
<Title>CC-Functions.Core</Title>