Browse Source

Reverted array after benchmark.

pull/476/head^2
Sebastian 6 years ago
parent
commit
c54c76c3c7
  1. 6
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs
  2. 6
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs
  3. 6
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppPatterns.cs
  4. 9
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
  5. 5
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
  6. 9
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs
  7. 7
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs
  8. 16
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs
  9. 3
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs
  10. 6
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs
  11. 213
      backend/src/Squidex.Infrastructure/Collections/ArrayDictionary{TKey,TValue}.cs
  12. 6
      backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary.cs
  13. 150
      backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary{TKey,TValue}.cs
  14. 3
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs

6
backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs

@ -13,7 +13,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps namespace Squidex.Domain.Apps.Core.Apps
{ {
public sealed class AppClients : ArrayDictionary<string, AppClient> public sealed class AppClients : ImmutableDictionary<string, AppClient>
{ {
public static readonly AppClients Empty = new AppClients(); public static readonly AppClients Empty = new AppClients();
@ -21,8 +21,8 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
} }
public AppClients(KeyValuePair<string, AppClient>[] items) public AppClients(Dictionary<string, AppClient> inner)
: base(items) : base(inner)
{ {
} }

6
backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps namespace Squidex.Domain.Apps.Core.Apps
{ {
public sealed class AppContributors : ArrayDictionary<string, string> public sealed class AppContributors : ImmutableDictionary<string, string>
{ {
public static readonly AppContributors Empty = new AppContributors(); public static readonly AppContributors Empty = new AppContributors();
@ -20,8 +20,8 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
} }
public AppContributors(KeyValuePair<string, string>[] items) public AppContributors(Dictionary<string, string> inner)
: base(items) : base(inner)
{ {
} }

6
backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppPatterns.cs

@ -13,7 +13,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps namespace Squidex.Domain.Apps.Core.Apps
{ {
public sealed class AppPatterns : ArrayDictionary<Guid, AppPattern> public sealed class AppPatterns : ImmutableDictionary<Guid, AppPattern>
{ {
public static readonly AppPatterns Empty = new AppPatterns(); public static readonly AppPatterns Empty = new AppPatterns();
@ -21,8 +21,8 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
} }
public AppPatterns(KeyValuePair<Guid, AppPattern>[] items) public AppPatterns(Dictionary<Guid, AppPattern> inner)
: base(items) : base(inner)
{ {
} }

9
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs

@ -29,14 +29,9 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
protected override AppClients ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) protected override AppClients ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{ {
var json = serializer.Deserialize<Dictionary<string, JsonAppClient>>(reader); var json = serializer.Deserialize<Dictionary<string, JsonAppClient>>(reader)!;
return new AppClients(json.Select(Convert).ToArray()); return new AppClients(json.ToDictionary(x => x.Key, x => x.Value.ToClient()));
}
private static KeyValuePair<string, AppClient> Convert(KeyValuePair<string, JsonAppClient> kvp)
{
return new KeyValuePair<string, AppClient>(kvp.Key, kvp.Value.ToClient());
} }
} }
} }

5
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs

@ -7,7 +7,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft; using Squidex.Infrastructure.Json.Newtonsoft;
@ -29,9 +28,9 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
protected override AppContributors ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) protected override AppContributors ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{ {
var json = serializer.Deserialize<Dictionary<string, string>>(reader); var json = serializer.Deserialize<Dictionary<string, string>>(reader)!;
return new AppContributors(json.ToArray()); return new AppContributors(json!);
} }
} }
} }

9
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs

@ -29,14 +29,9 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
protected override AppPatterns ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) protected override AppPatterns ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{ {
var json = serializer.Deserialize<Dictionary<Guid, JsonAppPattern>>(reader); var json = serializer.Deserialize<Dictionary<Guid, JsonAppPattern>>(reader)!;
return new AppPatterns(json.Select(Convert).ToArray()); return new AppPatterns(json.ToDictionary(x => x.Key, x => x.Value.ToPattern()));
}
private static KeyValuePair<Guid, AppPattern> Convert(KeyValuePair<Guid, JsonAppPattern> kvp)
{
return new KeyValuePair<Guid, AppPattern>(kvp.Key, kvp.Value.ToPattern());
} }
} }
} }

7
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs

@ -37,12 +37,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
return Roles.Empty; return Roles.Empty;
} }
return new Roles(json.Select(Convert)); return new Roles(json.ToDictionary(x => x.Key, x => new Role(x.Key, new PermissionSet(x.Value))));
}
private static KeyValuePair<string, Role> Convert(KeyValuePair<string, string[]> kvp)
{
return new KeyValuePair<string, Role>(kvp.Key, new Role(kvp.Key, new PermissionSet(kvp.Value)));
} }
} }
} }

16
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs

@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
public sealed class Roles public sealed class Roles
{ {
private readonly ArrayDictionary<string, Role> inner; private readonly ImmutableDictionary<string, Role> inner;
public static readonly IReadOnlyDictionary<string, Role> Defaults = new Dictionary<string, Role> public static readonly IReadOnlyDictionary<string, Role> Defaults = new Dictionary<string, Role>
{ {
@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Core.Apps
Clean(Permissions.AppWorkflows))) Clean(Permissions.AppWorkflows)))
}; };
public static readonly Roles Empty = new Roles(new ArrayDictionary<string, Role>()); public static readonly Roles Empty = new Roles(new ImmutableDictionary<string, Role>());
public int CustomCount public int CustomCount
{ {
@ -70,14 +70,14 @@ namespace Squidex.Domain.Apps.Core.Apps
get { return inner.Values.Union(Defaults.Values); } get { return inner.Values.Union(Defaults.Values); }
} }
private Roles(ArrayDictionary<string, Role> roles) private Roles(ImmutableDictionary<string, Role> roles)
{ {
inner = roles; inner = roles;
} }
public Roles(IEnumerable<KeyValuePair<string, Role>> items) public Roles(Dictionary<string, Role> roles)
{ {
inner = new ArrayDictionary<string, Role>(Cleaned(items)); inner = new ImmutableDictionary<string, Role>(Cleaned(roles));
} }
[Pure] [Pure]
@ -172,12 +172,12 @@ namespace Squidex.Domain.Apps.Core.Apps
return permission.Substring(1); return permission.Substring(1);
} }
private static KeyValuePair<string, Role>[] Cleaned(IEnumerable<KeyValuePair<string, Role>> items) private static Dictionary<string, Role> Cleaned(Dictionary<string, Role> inner)
{ {
return items.Where(x => !Defaults.ContainsKey(x.Key)).ToArray(); return inner.Where(x => !Defaults.ContainsKey(x.Key)).ToDictionary(x => x.Key, x => x.Value);
} }
private Roles Create(ArrayDictionary<string, Role> newRoles) private Roles Create(ImmutableDictionary<string, Role> newRoles)
{ {
return ReferenceEquals(inner, newRoles) ? this : new Roles(newRoles); return ReferenceEquals(inner, newRoles) ? this : new Roles(newRoles);
} }

3
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs

@ -7,7 +7,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft; using Squidex.Infrastructure.Json.Newtonsoft;
@ -31,7 +30,7 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
{ {
var json = serializer.Deserialize<Dictionary<Guid, Workflow>>(reader); var json = serializer.Deserialize<Dictionary<Guid, Workflow>>(reader);
return new Workflows(json.ToArray()); return new Workflows(json!);
} }
} }
} }

6
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs

@ -14,7 +14,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Contents namespace Squidex.Domain.Apps.Core.Contents
{ {
public sealed class Workflows : ArrayDictionary<Guid, Workflow> public sealed class Workflows : ImmutableDictionary<Guid, Workflow>
{ {
public static readonly Workflows Empty = new Workflows(); public static readonly Workflows Empty = new Workflows();
@ -22,8 +22,8 @@ namespace Squidex.Domain.Apps.Core.Contents
{ {
} }
public Workflows(KeyValuePair<Guid, Workflow>[] items) public Workflows(Dictionary<Guid, Workflow> inner)
: base(items) : base(inner)
{ {
} }

213
backend/src/Squidex.Infrastructure/Collections/ArrayDictionary{TKey,TValue}.cs

@ -1,213 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
#pragma warning disable IDE0044 // Add readonly modifier
#pragma warning disable RECS0108 // Warns about static fields in generic types
namespace Squidex.Infrastructure.Collections
{
public class ArrayDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> where TKey : notnull
{
private static readonly KeyValuePair<TKey, TValue>[] EmptyItems = Array.Empty<KeyValuePair<TKey, TValue>>();
private readonly IEqualityComparer<TKey> keyComparer;
private KeyValuePair<TKey, TValue>[] items;
public TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out var value))
{
throw new KeyNotFoundException();
}
return value;
}
}
public IEnumerable<TKey> Keys
{
get { return items.Select(x => x.Key); }
}
public IEnumerable<TValue> Values
{
get { return items.Select(x => x.Value); }
}
public int Count
{
get { return items.Length; }
}
public ArrayDictionary()
: this(EqualityComparer<TKey>.Default, EmptyItems)
{
}
public ArrayDictionary(KeyValuePair<TKey, TValue>[] items)
: this(EqualityComparer<TKey>.Default, items)
{
}
public ArrayDictionary(IEqualityComparer<TKey> keyComparer, KeyValuePair<TKey, TValue>[] items)
{
Guard.NotNull(items);
Guard.NotNull(keyComparer);
this.items = items;
this.keyComparer = keyComparer;
}
public bool IsUnchanged(KeyValuePair<TKey, TValue>[] values)
{
return ReferenceEquals(values, items);
}
public ArrayDictionary<TKey, TValue> With(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null)
{
return With<ArrayDictionary<TKey, TValue>>(key, value, valueComparer);
}
public TArray With<TArray>(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null) where TArray : ArrayDictionary<TKey, TValue>
{
var index = IndexOf(key);
if (index < 0)
{
var result = new KeyValuePair<TKey, TValue>[items.Length + 1];
Array.Copy(items, 0, result, 0, items.Length);
result[^1] = new KeyValuePair<TKey, TValue>(key, value);
return Create<TArray>(result);
}
var existing = items[index].Value;
if (valueComparer == null || !valueComparer.Equals(value, existing))
{
var result = new KeyValuePair<TKey, TValue>[items.Length];
Array.Copy(items, 0, result, 0, items.Length);
result[index] = new KeyValuePair<TKey, TValue>(key, value);
return Create<TArray>(result);
}
return Self<TArray>();
}
public ArrayDictionary<TKey, TValue> Without(TKey key)
{
return Without<ArrayDictionary<TKey, TValue>>(key);
}
public TArray Without<TArray>(TKey key) where TArray : ArrayDictionary<TKey, TValue>
{
var index = IndexOf(key);
if (index < 0)
{
return Self<TArray>();
}
if (Count == 1)
{
return Create<TArray>(EmptyItems);
}
var result = new KeyValuePair<TKey, TValue>[items.Length - 1];
Array.Copy(items, 0, result, 0, index);
Array.Copy(items, index + 1, result, index, items.Length - index - 1);
return Create<TArray>(result);
}
private TArray Self<TArray>() where TArray : ArrayDictionary<TKey, TValue>
{
return (this as TArray)!;
}
private TArray Create<TArray>(KeyValuePair<TKey, TValue>[] newItems) where TArray : ArrayDictionary<TKey, TValue>
{
if (ReferenceEquals(items, newItems))
{
return Self<TArray>();
}
var newClone = (TArray)MemberwiseClone();
newClone.items = newItems;
return newClone;
}
public bool ContainsKey(TKey key)
{
var index = IndexOf(key);
return index >= 0;
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
var index = IndexOf(key);
if (index >= 0)
{
value = items[index].Value;
return true;
}
else
{
value = default!;
return false;
}
}
private int IndexOf(TKey key)
{
for (var i = 0; i < items.Length; i++)
{
if (keyComparer.Equals(items[i].Key, key))
{
return i;
}
}
return -1;
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerable(items).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return items.GetEnumerator();
}
private static IEnumerable<T2> GetEnumerable<T2>(IEnumerable<T2> array)
{
return array;
}
}
}

6
backend/src/Squidex.Infrastructure/Collections/ArrayDictionary.cs → backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary.cs

@ -11,11 +11,11 @@ using System.Linq;
namespace Squidex.Infrastructure.Collections namespace Squidex.Infrastructure.Collections
{ {
public static class ArrayDictionary public static class ImmutableDictionary
{ {
public static ArrayDictionary<TKey, TValue> ToArrayDictionary<TKey, TValue>(this IEnumerable<TValue> source, Func<TValue, TKey> keyExtractor) where TKey : notnull public static ImmutableDictionary<TKey, TValue> ToImmutableDictionary<TKey, TValue>(this IEnumerable<TValue> source, Func<TValue, TKey> keyExtractor) where TKey : notnull
{ {
return new ArrayDictionary<TKey, TValue>(source.Select(x => new KeyValuePair<TKey, TValue>(keyExtractor(x), x)).ToArray()); return new ImmutableDictionary<TKey, TValue>(source.ToDictionary(keyExtractor));
} }
} }
} }

150
backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary{TKey,TValue}.cs

@ -0,0 +1,150 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#pragma warning disable IDE0044 // Add readonly modifier
#pragma warning disable RECS0108 // Warns about static fields in generic types
namespace Squidex.Infrastructure.Collections
{
public class ImmutableDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> where TKey : notnull
{
private static readonly Dictionary<TKey, TValue> EmptyInner = new Dictionary<TKey, TValue>();
private Dictionary<TKey, TValue> inner;
public TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out var value))
{
throw new KeyNotFoundException();
}
return value;
}
}
public IEnumerable<TKey> Keys
{
get { return inner.Keys; }
}
public IEnumerable<TValue> Values
{
get { return inner.Values; }
}
public int Count
{
get { return inner.Count; }
}
public ImmutableDictionary()
: this(EmptyInner)
{
}
public ImmutableDictionary(Dictionary<TKey, TValue> inner)
{
Guard.NotNull(inner);
this.inner = inner;
}
public ImmutableDictionary<TKey, TValue> With(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null)
{
return With<ImmutableDictionary<TKey, TValue>>(key, value, valueComparer);
}
public TArray With<TArray>(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null) where TArray : ImmutableDictionary<TKey, TValue>
{
if (!TryGetValue(key, out var found) || !IsEqual(value, found, valueComparer))
{
var clone = new Dictionary<TKey, TValue>(inner)
{
[key] = value
};
return Create<TArray>(clone);
}
return Self<TArray>();
}
private static bool IsEqual(TValue lhs, TValue rhs, IEqualityComparer<TValue>? comparer = null)
{
return comparer == null || comparer.Equals(lhs, rhs);
}
public ImmutableDictionary<TKey, TValue> Without(TKey key)
{
return Without<ImmutableDictionary<TKey, TValue>>(key);
}
public TArray Without<TArray>(TKey key) where TArray : ImmutableDictionary<TKey, TValue>
{
if (!inner.ContainsKey(key))
{
return Self<TArray>();
}
if (Count == 1)
{
return Create<TArray>(EmptyInner);
}
var clone = new Dictionary<TKey, TValue>(inner);
clone.Remove(key);
return Create<TArray>(clone);
}
private TArray Self<TArray>() where TArray : ImmutableDictionary<TKey, TValue>
{
return (this as TArray)!;
}
private TArray Create<TArray>(Dictionary<TKey, TValue> clone) where TArray : ImmutableDictionary<TKey, TValue>
{
var newClone = (TArray)MemberwiseClone();
newClone.inner = clone;
return newClone;
}
public bool ContainsKey(TKey key)
{
return inner.ContainsKey(key);
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
return inner.TryGetValue(key, out value);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerable(inner).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return inner.GetEnumerator();
}
private static IEnumerable<TItem> GetEnumerable<TItem>(IEnumerable<TItem> collection)
{
return collection;
}
}
}

3
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
@ -30,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
[Fact] [Fact]
public void Should_create_roles_without_defaults() public void Should_create_roles_without_defaults()
{ {
var roles = new Roles(Roles.Defaults.ToArray()); var roles = new Roles(new Dictionary<string, Role>(Roles.Defaults));
Assert.Equal(0, roles.CustomCount); Assert.Equal(0, roles.CustomCount);
} }

Loading…
Cancel
Save