Browse Source

Event consumers simplified.

pull/199/head
Sebastian Stehle 8 years ago
parent
commit
797f6a164b
  1. 56
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrain.cs
  2. 24
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState.cs
  3. 2
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Apps.cs
  4. 9
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Rules.cs
  5. 19
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Schemas.cs
  6. 18
      src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrain.cs
  7. 17
      src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrainState.cs
  8. 16
      src/Squidex.Infrastructure/CollectionExtensions.cs
  9. 1
      src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  10. 1
      src/Squidex/Config/Web/WebServices.cs
  11. 31
      tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs

56
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrain.cs

@ -18,14 +18,12 @@ using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.State.Grains namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public class AppStateGrain : StatefulObject<AppStateGrainState> public class AppStateGrain : StatefulObject<AppStateGrainState>
{ {
private readonly FieldRegistry fieldRegistry; private readonly FieldRegistry fieldRegistry;
private readonly TaskFactory taskFactory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(1));
private Exception exception; private Exception exception;
public AppStateGrain(FieldRegistry fieldRegistry) public AppStateGrain(FieldRegistry fieldRegistry)
@ -52,68 +50,48 @@ namespace Squidex.Domain.Apps.Read.State.Grains
} }
public virtual Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(Guid id) public virtual Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(Guid id)
{
return taskFactory.StartNew(() =>
{ {
var schema = State.FindSchema(x => x.Id == id && !x.IsDeleted); var schema = State.FindSchema(x => x.Id == id && !x.IsDeleted);
return (State.GetApp(), schema); return Task.FromResult((State.GetApp(), schema));
});
} }
public virtual Task<IAppEntity> GetAppAsync() public virtual Task<IAppEntity> GetAppAsync()
{ {
return taskFactory.StartNew(() => var result = State.GetApp();
{
var value = State.GetApp();
return value; return Task.FromResult(result);
});
} }
public virtual Task<List<IRuleEntity>> GetRulesAsync() public virtual Task<List<IRuleEntity>> GetRulesAsync()
{ {
return taskFactory.StartNew(() => var result = State.FindRules();
{
var value = State.FindRules();
return value; return Task.FromResult(result);
});
} }
public virtual Task<List<ISchemaEntity>> GetSchemasAsync() public virtual Task<List<ISchemaEntity>> GetSchemasAsync()
{ {
return taskFactory.StartNew(() => var result = State.FindSchemas(x => !x.IsDeleted);
{
var value = State.FindSchemas(x => !x.IsDeleted);
return value; return Task.FromResult(result);
});
} }
public virtual Task<ISchemaEntity> GetSchemaAsync(Guid id, bool provideDeleted = false) public virtual Task<ISchemaEntity> GetSchemaAsync(Guid id, bool provideDeleted = false)
{ {
return taskFactory.StartNew(() => var result = State.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted));
{
var value = State.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted));
return value; return Task.FromResult(result);
});
} }
public virtual Task<ISchemaEntity> GetSchemaAsync(string name, bool provideDeleted = false) public virtual Task<ISchemaEntity> GetSchemaAsync(string name, bool provideDeleted = false)
{ {
return taskFactory.StartNew(() => var result = State.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted));
{
var value = State.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted));
return value; return Task.FromResult(result);
});
} }
public virtual Task HandleAsync(Envelope<IEvent> message) public async virtual Task HandleAsync(Envelope<IEvent> message)
{
return taskFactory.StartNew(async () =>
{ {
if (exception != null) if (exception != null)
{ {
@ -127,13 +105,11 @@ namespace Squidex.Domain.Apps.Read.State.Grains
} }
} }
if (message.Payload is AppEvent appEvent) if (message.Payload is AppEvent appEvent && (State.App == null || State.App.Id == appEvent.AppId.Id))
{
if (State.App == null || State.App.Id == appEvent.AppId.Id)
{ {
try try
{ {
State.Apply(message); State = State.Apply(message);
await WriteStateAsync(); await WriteStateAsync();
} }
@ -141,13 +117,11 @@ namespace Squidex.Domain.Apps.Read.State.Grains
{ {
await ReadStateAsync(); await ReadStateAsync();
State.Apply(message); State = State.Apply(message);
await WriteStateAsync(); await WriteStateAsync();
} }
} }
} }
}).Unwrap();
}
} }
} }

24
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState.cs

@ -8,18 +8,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules; using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Read.State.Grains namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public sealed partial class AppStateGrainState public sealed partial class AppStateGrainState : Cloneable<AppStateGrainState>
{ {
private FieldRegistry registry; private FieldRegistry registry;
@ -27,10 +29,10 @@ namespace Squidex.Domain.Apps.Read.State.Grains
public JsonAppEntity App { get; set; } public JsonAppEntity App { get; set; }
[JsonProperty] [JsonProperty]
public Dictionary<Guid, JsonRuleEntity> Rules { get; set; } public ImmutableDictionary<Guid, JsonRuleEntity> Rules { get; set; } = ImmutableDictionary<Guid, JsonRuleEntity>.Empty;
[JsonProperty] [JsonProperty]
public Dictionary<Guid, JsonSchemaEntity> Schemas { get; set; } public ImmutableDictionary<Guid, JsonSchemaEntity> Schemas { get; set; } = ImmutableDictionary<Guid, JsonSchemaEntity>.Empty;
public void SetRegistry(FieldRegistry registry) public void SetRegistry(FieldRegistry registry)
{ {
@ -57,21 +59,17 @@ namespace Squidex.Domain.Apps.Read.State.Grains
return Rules?.Values.OfType<IRuleEntity>().ToList() ?? new List<IRuleEntity>(); return Rules?.Values.OfType<IRuleEntity>().ToList() ?? new List<IRuleEntity>();
} }
public void Reset() public AppStateGrainState Apply(Envelope<IEvent> envelope)
{ {
Rules = new Dictionary<Guid, JsonRuleEntity>(); return Clone(c =>
Schemas = new Dictionary<Guid, JsonSchemaEntity>();
}
public void Apply(Envelope<IEvent> envelope)
{ {
this.DispatchAction(envelope.Payload, envelope.Headers); c.DispatchAction(envelope.Payload, envelope.Headers);
if (App != null) if (c.App != null)
{ {
App.Etag = Guid.NewGuid().ToString(); c.App.Etag = Guid.NewGuid().ToString();
} }
});
} }
} }
} }

2
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Apps.cs

@ -21,8 +21,6 @@ namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public void On(AppCreated @event, EnvelopeHeaders headers) public void On(AppCreated @event, EnvelopeHeaders headers)
{ {
Reset();
App = EntityMapper.Create<JsonAppEntity>(@event, headers, a => App = EntityMapper.Create<JsonAppEntity>(@event, headers, a =>
{ {
SimpleMapper.Map(@event, a); SimpleMapper.Map(@event, a);

9
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Rules.cs

@ -9,6 +9,7 @@
using System; using System;
using Squidex.Domain.Apps.Events.Rules; using Squidex.Domain.Apps.Events.Rules;
using Squidex.Domain.Apps.Events.Rules.Utils; using Squidex.Domain.Apps.Events.Rules.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Domain.Apps.Read.State.Grains namespace Squidex.Domain.Apps.Read.State.Grains
@ -17,10 +18,12 @@ namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public void On(RuleCreated @event, EnvelopeHeaders headers) public void On(RuleCreated @event, EnvelopeHeaders headers)
{ {
Rules[@event.RuleId] = EntityMapper.Create<JsonRuleEntity>(@event, headers, r => var id = @event.RuleId;
Rules.SetItem(id, EntityMapper.Create<JsonRuleEntity>(@event, headers, r =>
{ {
r.RuleDef = RuleEventDispatcher.Create(@event); r.RuleDef = RuleEventDispatcher.Create(@event);
}); }));
} }
public void On(RuleUpdated @event, EnvelopeHeaders headers) public void On(RuleUpdated @event, EnvelopeHeaders headers)
@ -56,7 +59,7 @@ namespace Squidex.Domain.Apps.Read.State.Grains
{ {
var id = @event.RuleId; var id = @event.RuleId;
Rules[id] = Rules[id].Clone().Update(@event, headers, updater); Rules = Rules.SetItem(id, x => x.Clone().Update(@event, headers, updater));
} }
} }
} }

19
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Schemas.cs

@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Events.Schemas.Old; using Squidex.Domain.Apps.Events.Schemas.Old;
using Squidex.Domain.Apps.Events.Schemas.Utils; using Squidex.Domain.Apps.Events.Schemas.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -22,12 +23,14 @@ namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public void On(SchemaCreated @event, EnvelopeHeaders headers) public void On(SchemaCreated @event, EnvelopeHeaders headers)
{ {
Schemas[@event.SchemaId.Id] = EntityMapper.Create<JsonSchemaEntity>(@event, headers, s => var id = @event.SchemaId.Id;
Schemas = Schemas.SetItem(id, EntityMapper.Create<JsonSchemaEntity>(@event, headers, s =>
{ {
s.SchemaDef = SchemaEventDispatcher.Create(@event, registry); s.SchemaDef = SchemaEventDispatcher.Create(@event, registry);
SimpleMapper.Map(@event, s); SimpleMapper.Map(@event, s);
}); }));
} }
public void On(SchemaPublished @event, EnvelopeHeaders headers) public void On(SchemaPublished @event, EnvelopeHeaders headers)
@ -70,11 +73,6 @@ namespace Squidex.Domain.Apps.Read.State.Grains
}); });
} }
public void On(SchemaDeleted @event)
{
Schemas.Remove(@event.SchemaId.Id);
}
public void On(FieldAdded @event, EnvelopeHeaders headers) public void On(FieldAdded @event, EnvelopeHeaders headers)
{ {
UpdateSchema(@event, headers, s => UpdateSchema(@event, headers, s =>
@ -149,11 +147,16 @@ namespace Squidex.Domain.Apps.Read.State.Grains
UpdateSchema(@event, headers); UpdateSchema(@event, headers);
} }
public void On(SchemaDeleted @event, EnvelopeHeaders headers)
{
Schemas = Schemas.Remove(@event.SchemaId.Id);
}
private void UpdateSchema(SchemaEvent @event, EnvelopeHeaders headers, Action<JsonSchemaEntity> updater = null) private void UpdateSchema(SchemaEvent @event, EnvelopeHeaders headers, Action<JsonSchemaEntity> updater = null)
{ {
var id = @event.SchemaId.Id; var id = @event.SchemaId.Id;
Schemas[id] = Schemas[id].Clone().Update(@event, headers, updater); Schemas = Schemas.SetItem(id, x => x.Clone().Update(@event, headers, updater));
} }
} }
} }

18
src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrain.cs

@ -10,40 +10,28 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.State.Grains namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public sealed class AppUserGrain : StatefulObject<AppUserGrainState> public sealed class AppUserGrain : StatefulObject<AppUserGrainState>
{ {
private readonly TaskFactory taskFactory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(1));
public Task AddAppAsync(string appName) public Task AddAppAsync(string appName)
{ {
return taskFactory.StartNew(() => State = State.AddApp(appName);
{
State.AppNames.Add(appName);
return WriteStateAsync(); return WriteStateAsync();
}).Unwrap();
} }
public Task RemoveAppAsync(string appName) public Task RemoveAppAsync(string appName)
{ {
return taskFactory.StartNew(() => State = State.RemoveApp(appName);
{
State.AppNames.Remove(appName);
return WriteStateAsync(); return WriteStateAsync();
}).Unwrap();
} }
public Task<List<string>> GetAppNamesAsync() public Task<List<string>> GetAppNamesAsync()
{ {
return taskFactory.StartNew(() => return Task.FromResult(State.AppNames.ToList());
{
return State.AppNames.ToList();
});
} }
} }
} }

17
src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrainState.cs

@ -6,14 +6,25 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Immutable;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Grains namespace Squidex.Domain.Apps.Read.State.Grains
{ {
public sealed class AppUserGrainState public sealed class AppUserGrainState : Cloneable<AppUserGrainState>
{ {
[JsonProperty] [JsonProperty]
public HashSet<string> AppNames { get; set; } = new HashSet<string>(); public ImmutableHashSet<string> AppNames { get; set; } = ImmutableHashSet<string>.Empty;
public AppUserGrainState AddApp(string appName)
{
return Clone(c => c.AppNames = c.AppNames.Add(appName));
}
public AppUserGrainState RemoveApp(string appName)
{
return Clone(c => c.AppNames = c.AppNames.Remove(appName));
}
} }
} }

16
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -8,12 +8,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace Squidex.Infrastructure namespace Squidex.Infrastructure
{ {
public static class CollectionExtensions public static class CollectionExtensions
{ {
public static ImmutableDictionary<TKey, TValue> SetItem<TKey, TValue>(this ImmutableDictionary<TKey, TValue> dictionary, TKey key, Func<TValue, TValue> updater)
{
if (dictionary.TryGetValue(key, out var value))
{
var newValue = updater(value);
if (!Equals(newValue, value))
{
return dictionary.SetItem(key, newValue);
}
}
return dictionary;
}
public static bool TryGetValue<TKey, TValue, TBase>(this IReadOnlyDictionary<TKey, TValue> values, TKey key, out TBase item) where TValue : TBase public static bool TryGetValue<TKey, TValue, TBase>(this IReadOnlyDictionary<TKey, TValue> values, TKey key, out TBase item) where TValue : TBase
{ {
if (values.TryGetValue(key, out var value)) if (values.TryGetValue(key, out var value))

1
src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -15,6 +15,7 @@
<PackageReference Include="RefactoringEssentials" Version="5.4.0" /> <PackageReference Include="RefactoringEssentials" Version="5.4.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0001" /> <PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0001" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" /> <PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.Linq" Version="4.3.0" /> <PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" /> <PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" /> <PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" />

1
src/Squidex/Config/Web/WebServices.cs

@ -17,6 +17,7 @@ namespace Squidex.Config.Web
public static void AddMyMvc(this IServiceCollection services) public static void AddMyMvc(this IServiceCollection services)
{ {
services.AddSingletonAs<FileCallbackResultExecutor>(); services.AddSingletonAs<FileCallbackResultExecutor>();
services.AddSingletonAs<AppApiFilter>(); services.AddSingletonAs<AppApiFilter>();
services.AddSingletonAs<ApiCostsFilter>(); services.AddSingletonAs<ApiCostsFilter>();

31
tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs

@ -7,6 +7,7 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using Xunit; using Xunit;
namespace Squidex.Infrastructure namespace Squidex.Infrastructure
@ -268,5 +269,35 @@ namespace Squidex.Infrastructure
Assert.Equal(source, target); Assert.Equal(source, target);
} }
[Fact]
public void Should_return_same_dictionary_if_item_to_replace_not_found()
{
var dict_0 = ImmutableDictionary<int, int>.Empty;
var dict_1 = dict_0.SetItem(1, x => x);
Assert.Same(dict_0, dict_1);
}
[Fact]
public void Should_return_same_dictionary_if_replaced_item_is_same()
{
var dict_0 = ImmutableDictionary<int, int>.Empty;
var dict_1 = dict_0.SetItem(1, 1);
var dict_2 = dict_1.SetItem(1, x => x);
Assert.Same(dict_1, dict_2);
}
[Fact]
public void Should_return_new_dictionary_if_updated_item_is_different()
{
var dict_0 = ImmutableDictionary<int, int>.Empty;
var dict_1 = dict_0.SetItem(2, 2);
var dict_2 = dict_1.SetItem(2, x => 2 * x);
Assert.NotSame(dict_1, dict_2);
Assert.Equal(4, dict_2[2]);
}
} }
} }
Loading…
Cancel
Save