Browse Source

First domain grains.

pull/249/head
Sebastian Stehle 9 years ago
parent
commit
a9305134f5
  1. 130
      src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  2. 89
      src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs
  3. 15
      src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs
  4. 15
      src/Squidex.Domain.Apps.Entities/Rules/IRuleGrain.cs
  5. 89
      src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs
  6. 86
      src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs
  7. 135
      src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs
  8. 15
      src/Squidex.Domain.Apps.Entities/Schemas/ISchemaGrain.cs
  9. 194
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaCommandMiddleware.cs
  10. 229
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs
  11. 330
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs
  12. 3
      src/Squidex.Infrastructure/Assets/AssetFile.cs
  13. 204
      src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs
  14. 46
      src/Squidex.Infrastructure/Commands/GrainCommandMiddleware.cs
  15. 20
      src/Squidex.Infrastructure/Commands/IDomainObjectGrain.cs
  16. 18
      src/Squidex.Infrastructure/Orleans/J.cs
  17. 17
      src/Squidex.Infrastructure/Orleans/JExtensions.cs
  18. 85
      src/Squidex.Infrastructure/Orleans/J{T}.cs
  19. 34
      src/Squidex/Config/Domain/SerializationServices.cs
  20. 17
      src/Squidex/Config/Domain/WriteServices.cs
  21. 4
      src/Squidex/Config/Orleans/SiloWrapper.cs

130
src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -7,109 +7,81 @@
using System;
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.Assets.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetCommandMiddleware : ICommandMiddleware
public sealed class AssetCommandMiddleware : GrainCommandMiddleware<AssetCommand, IAssetGrain>
{
private readonly IAggregateHandler handler;
private readonly IAssetStore assetStore;
private readonly IAssetThumbnailGenerator assetThumbnailGenerator;
public AssetCommandMiddleware(
IAggregateHandler handler,
IGrainFactory grainFactory,
IAssetStore assetStore,
IAssetThumbnailGenerator assetThumbnailGenerator)
: base(grainFactory)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(assetStore, nameof(assetStore));
Guard.NotNull(assetThumbnailGenerator, nameof(assetThumbnailGenerator));
this.handler = handler;
this.assetStore = assetStore;
this.assetThumbnailGenerator = assetThumbnailGenerator;
}
protected async Task On(CreateAsset command, CommandContext context)
public async override Task HandleAsync(CommandContext context, Func<Task> next)
{
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
try
switch (context.Command)
{
var asset = await handler.CreateSyncedAsync<AssetDomainObject>(context, async a =>
{
GuardAsset.CanCreate(command);
a.Create(command);
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
context.Complete(EntityCreatedResult.Create(command.AssetId, a.Version));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.Snapshot.FileVersion, null);
}
finally
{
await assetStore.DeleteTemporaryAsync(context.ContextId.ToString());
}
}
protected async Task On(UpdateAsset command, CommandContext context)
{
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
try
{
var asset = await handler.UpdateSyncedAsync<AssetDomainObject>(context, async a =>
{
GuardAsset.CanUpdate(command);
a.Update(command);
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
context.Complete(new AssetSavedResult(a.Version, a.Snapshot.FileVersion));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.Snapshot.FileVersion, null);
}
finally
{
await assetStore.DeleteTemporaryAsync(context.ContextId.ToString());
}
}
protected Task On(RenameAsset command, CommandContext context)
{
return handler.UpdateSyncedAsync<AssetDomainObject>(context, a =>
{
GuardAsset.CanRename(command, a.Snapshot.FileName);
a.Rename(command);
});
}
protected Task On(DeleteAsset command, CommandContext context)
{
return handler.UpdateSyncedAsync<AssetDomainObject>(context, a =>
{
GuardAsset.CanDelete(command);
a.Delete(command);
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
if (!await this.DispatchActionAsync(context.Command, context))
{
await next();
case CreateAsset createAsset:
{
createAsset.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(createAsset.File.OpenRead());
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), createAsset.File.OpenRead());
try
{
var result = await ExecuteCommandAsync(createAsset) as AssetSavedResult;
context.Complete(EntityCreatedResult.Create(createAsset.AssetId, result.Version));
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), createAsset.AssetId.ToString(), result.FileVersion, null);
}
finally
{
await assetStore.DeleteTemporaryAsync(context.ContextId.ToString());
}
break;
}
case UpdateAsset updateAsset:
{
updateAsset.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(updateAsset.File.OpenRead());
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), updateAsset.File.OpenRead());
try
{
var result = await ExecuteCommandAsync(updateAsset) as AssetSavedResult;
context.Complete(result);
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), updateAsset.AssetId.ToString(), result.FileVersion, null);
}
finally
{
await assetStore.DeleteTemporaryAsync(context.ContextId.ToString());
}
break;
}
default:
await base.HandleAsync(context, next);
break;
}
}
}

89
src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs → src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs

@ -5,22 +5,71 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.Assets.Guards;
using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Assets
{
public sealed class AssetDomainObject : SquidexDomainObjectBase<AssetState>
public sealed class AssetGrain : DomainObjectGrain<AssetState>, IAssetGrain
{
public AssetDomainObject Create(CreateAsset command)
public AssetGrain(IStore<Guid> store)
: base(store)
{
VerifyNotCreated();
}
public override Task<object> ExecuteAsync(IAggregateCommand command)
{
switch (command)
{
case CreateAsset createRule:
return CreateReturnAsync(createRule, c =>
{
GuardAsset.CanCreate(c);
Create(c);
return new AssetSavedResult(NewVersion, Snapshot.FileVersion);
});
case UpdateAsset updateRule:
return UpdateReturnAsync(updateRule, c =>
{
GuardAsset.CanUpdate(c);
Update(c);
return new AssetSavedResult(NewVersion, Snapshot.FileVersion);
});
case RenameAsset renameAsset:
return UpdateAsync(renameAsset, c =>
{
GuardAsset.CanRename(c, Snapshot.FileName);
Rename(c);
});
case DeleteAsset deleteAsset:
return UpdateAsync(deleteAsset, c =>
{
GuardAsset.CanDelete(c);
Delete(c);
});
default:
throw new NotSupportedException();
}
}
public void Create(CreateAsset command)
{
var @event = SimpleMapper.Map(command, new AssetCreated
{
FileName = command.File.FileName,
@ -33,13 +82,11 @@ namespace Squidex.Domain.Apps.Entities.Assets
});
RaiseEvent(@event);
return this;
}
public AssetDomainObject Update(UpdateAsset command)
public void Update(UpdateAsset command)
{
VerifyCreatedAndNotDeleted();
VerifyNotDeleted();
var @event = SimpleMapper.Map(command, new AssetUpdated
{
@ -52,26 +99,20 @@ namespace Squidex.Domain.Apps.Entities.Assets
});
RaiseEvent(@event);
return this;
}
public AssetDomainObject Delete(DeleteAsset command)
public void Delete(DeleteAsset command)
{
VerifyCreatedAndNotDeleted();
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = Snapshot.TotalSize }));
return this;
}
public AssetDomainObject Rename(RenameAsset command)
public void Rename(RenameAsset command)
{
VerifyCreatedAndNotDeleted();
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new AssetRenamed()));
return this;
}
private void RaiseEvent(AppEvent @event)
@ -84,19 +125,11 @@ namespace Squidex.Domain.Apps.Entities.Assets
RaiseEvent(Envelope.Create(@event));
}
private void VerifyNotCreated()
{
if (!string.IsNullOrWhiteSpace(Snapshot.FileName))
{
throw new DomainException("Asset has already been created.");
}
}
private void VerifyCreatedAndNotDeleted()
private void VerifyNotDeleted()
{
if (Snapshot.IsDeleted || string.IsNullOrWhiteSpace(Snapshot.FileName))
if (Snapshot.IsDeleted)
{
throw new DomainException("Asset has already been deleted or not created yet.");
throw new DomainException("Asset has already been deleted");
}
}

15
src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs

@ -0,0 +1,15 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Entities.Assets
{
public interface IAssetGrain : IDomainObjectGrain
{
}
}

15
src/Squidex.Domain.Apps.Entities/Rules/IRuleGrain.cs

@ -0,0 +1,15 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Entities.Rules
{
public interface IRuleGrain : IDomainObjectGrain
{
}
}

89
src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs

@ -1,89 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Rules.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Entities.Rules
{
public class RuleCommandMiddleware : ICommandMiddleware
{
private readonly IAggregateHandler handler;
private readonly IAppProvider appProvider;
public RuleCommandMiddleware(IAggregateHandler handler, IAppProvider appProvider)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(appProvider, nameof(appProvider));
this.handler = handler;
this.appProvider = appProvider;
}
protected Task On(CreateRule command, CommandContext context)
{
return handler.CreateSyncedAsync<RuleDomainObject>(context, async r =>
{
await GuardRule.CanCreate(command, appProvider);
r.Create(command);
});
}
protected Task On(UpdateRule command, CommandContext context)
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, async r =>
{
await GuardRule.CanUpdate(command, r.Snapshot.AppId.Id, appProvider);
r.Update(command);
});
}
protected Task On(EnableRule command, CommandContext context)
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r =>
{
GuardRule.CanEnable(command, r.Snapshot.RuleDef);
r.Enable(command);
});
}
protected Task On(DisableRule command, CommandContext context)
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r =>
{
GuardRule.CanDisable(command, r.Snapshot.RuleDef);
r.Disable(command);
});
}
protected Task On(DeleteRule command, CommandContext context)
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r =>
{
GuardRule.CanDelete(command);
r.Delete(command);
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
await this.DispatchActionAsync(context.Command, context);
await next();
}
}
}

86
src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs

@ -1,86 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Rules
{
public sealed class RuleDomainObject : SquidexDomainObjectBase<RuleState>
{
public void Create(CreateRule command)
{
VerifyNotCreated();
RaiseEvent(SimpleMapper.Map(command, new RuleCreated()));
}
public void Update(UpdateRule command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated()));
}
public void Enable(EnableRule command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled()));
}
public void Disable(DisableRule command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled()));
}
public void Delete(DeleteRule command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleDeleted()));
}
private void RaiseEvent(AppEvent @event)
{
if (@event.AppId == null)
{
@event.AppId = Snapshot.AppId;
}
RaiseEvent(Envelope.Create(@event));
}
private void VerifyNotCreated()
{
if (Snapshot.RuleDef != null)
{
throw new DomainException("Webhook has already been created.");
}
}
private void VerifyCreatedAndNotDeleted()
{
if (Snapshot.IsDeleted || Snapshot.RuleDef == null)
{
throw new DomainException("Webhook has already been deleted or not created yet.");
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

135
src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs

@ -0,0 +1,135 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Rules.Guards;
using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Rules
{
public sealed class RuleGrain : DomainObjectGrain<RuleState>, IRuleGrain
{
private readonly IAppProvider appProvider;
public RuleGrain(IStore<Guid> store, IAppProvider appProvider)
: base(store)
{
Guard.NotNull(appProvider, nameof(appProvider));
this.appProvider = appProvider;
}
public override Task<object> ExecuteAsync(IAggregateCommand command)
{
switch (command)
{
case CreateRule createRule:
return CreateAsync(createRule, c =>
{
GuardRule.CanCreate(c, appProvider);
Create(c);
});
case UpdateRule updateRule:
return UpdateAsync(updateRule, c =>
{
GuardRule.CanUpdate(c, Snapshot.AppId.Id, appProvider);
Update(c);
});
case EnableRule enableRule:
return UpdateAsync(enableRule, c =>
{
GuardRule.CanEnable(c, Snapshot.RuleDef);
Enable(c);
});
case DisableRule disableRule:
return UpdateAsync(disableRule, c =>
{
GuardRule.CanDisable(c, Snapshot.RuleDef);
Disable(c);
});
case DeleteRule deleteRule:
return UpdateAsync(deleteRule, c =>
{
GuardRule.CanDelete(deleteRule);
Delete(c);
});
default:
throw new NotSupportedException();
}
}
public void Create(CreateRule command)
{
RaiseEvent(SimpleMapper.Map(command, new RuleCreated()));
}
public void Update(UpdateRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated()));
}
public void Enable(EnableRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled()));
}
public void Disable(DisableRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled()));
}
public void Delete(DeleteRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleDeleted()));
}
private void RaiseEvent(AppEvent @event)
{
if (@event.AppId == null)
{
@event.AppId = Snapshot.AppId;
}
RaiseEvent(Envelope.Create(@event));
}
private void VerifyNotDeleted()
{
if (Snapshot.IsDeleted || Snapshot.RuleDef == null)
{
throw new DomainException("Webhook has already been deleted.");
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

15
src/Squidex.Domain.Apps.Entities/Schemas/ISchemaGrain.cs

@ -0,0 +1,15 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Entities.Schemas
{
public interface ISchemaGrain : IDomainObjectGrain
{
}
}

194
src/Squidex.Domain.Apps.Entities/Schemas/SchemaCommandMiddleware.cs

@ -1,194 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Entities.Schemas
{
public class SchemaCommandMiddleware : ICommandMiddleware
{
private readonly IAppProvider appProvider;
private readonly IAggregateHandler handler;
public SchemaCommandMiddleware(IAggregateHandler handler, IAppProvider appProvider)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(appProvider, nameof(appProvider));
this.handler = handler;
this.appProvider = appProvider;
}
protected Task On(CreateSchema command, CommandContext context)
{
return handler.CreateSyncedAsync<SchemaDomainObject>(context, async s =>
{
await GuardSchema.CanCreate(command, appProvider);
s.Create(command);
context.Complete(EntityCreatedResult.Create(command.SchemaId, s.Version));
});
}
protected Task On(AddField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanAdd(s.Snapshot.SchemaDef, command);
s.Add(command);
context.Complete(EntityCreatedResult.Create(s.Snapshot.SchemaDef.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version));
});
}
protected Task On(DeleteField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanDelete(s.Snapshot.SchemaDef, command);
s.DeleteField(command);
});
}
protected Task On(LockField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanLock(s.Snapshot.SchemaDef, command);
s.LockField(command);
});
}
protected Task On(HideField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanHide(s.Snapshot.SchemaDef, command);
s.HideField(command);
});
}
protected Task On(ShowField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanShow(s.Snapshot.SchemaDef, command);
s.ShowField(command);
});
}
protected Task On(DisableField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanDisable(s.Snapshot.SchemaDef, command);
s.DisableField(command);
});
}
protected Task On(EnableField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanEnable(s.Snapshot.SchemaDef, command);
s.EnableField(command);
});
}
protected Task On(UpdateField command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanUpdate(s.Snapshot.SchemaDef, command);
s.UpdateField(command);
});
}
protected Task On(ReorderFields command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanReorder(s.Snapshot.SchemaDef, command);
s.Reorder(command);
});
}
protected Task On(UpdateSchema command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanUpdate(s.Snapshot.SchemaDef, command);
s.Update(command);
});
}
protected Task On(PublishSchema command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanPublish(s.Snapshot.SchemaDef, command);
s.Publish(command);
});
}
protected Task On(UnpublishSchema command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanUnpublish(s.Snapshot.SchemaDef, command);
s.Unpublish(command);
});
}
protected Task On(ConfigureScripts command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanConfigureScripts(s.Snapshot.SchemaDef, command);
s.ConfigureScripts(command);
});
}
protected Task On(DeleteSchema command, CommandContext context)
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanDelete(s.Snapshot.SchemaDef, command);
s.Delete(command);
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
await this.DispatchActionAsync(context.Command, context);
await next();
}
}
}

229
src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs

@ -1,229 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.Schemas.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Schemas
{
public sealed class SchemaDomainObject : SquidexDomainObjectBase<SchemaState>
{
private readonly FieldRegistry registry;
public SchemaDomainObject(FieldRegistry registry)
{
Guard.NotNull(registry, nameof(registry));
this.registry = registry;
}
public SchemaDomainObject Create(CreateSchema command)
{
VerifyNotCreated();
var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId<Guid>(command.SchemaId, command.Name) });
if (command.Fields != null)
{
@event.Fields = new List<SchemaCreatedField>();
foreach (var commandField in command.Fields)
{
var eventField = SimpleMapper.Map(commandField, new SchemaCreatedField());
@event.Fields.Add(eventField);
}
}
RaiseEvent(@event);
return this;
}
public SchemaDomainObject Add(AddField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) }));
return this;
}
public SchemaDomainObject UpdateField(UpdateField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated()));
return this;
}
public SchemaDomainObject LockField(LockField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, new FieldLocked());
return this;
}
public SchemaDomainObject HideField(HideField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, new FieldHidden());
return this;
}
public SchemaDomainObject ShowField(ShowField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, new FieldShown());
return this;
}
public SchemaDomainObject DisableField(DisableField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, new FieldDisabled());
return this;
}
public SchemaDomainObject EnableField(EnableField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, new FieldEnabled());
return this;
}
public SchemaDomainObject DeleteField(DeleteField command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(command, new FieldDeleted());
return this;
}
public SchemaDomainObject Reorder(ReorderFields command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered()));
return this;
}
public SchemaDomainObject Publish(PublishSchema command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished()));
return this;
}
public SchemaDomainObject Unpublish(UnpublishSchema command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished()));
return this;
}
public SchemaDomainObject ConfigureScripts(ConfigureScripts command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
return this;
}
public SchemaDomainObject Delete(DeleteSchema command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted()));
return this;
}
public SchemaDomainObject Update(UpdateSchema command)
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated()));
return this;
}
private void RaiseEvent(FieldCommand fieldCommand, FieldEvent @event)
{
SimpleMapper.Map(fieldCommand, @event);
if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field))
{
@event.FieldId = new NamedId<long>(field.Id, field.Name);
}
RaiseEvent(@event);
}
private void RaiseEvent(SchemaEvent @event)
{
if (@event.SchemaId == null)
{
@event.SchemaId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name);
}
if (@event.AppId == null)
{
@event.AppId = Snapshot.AppId;
}
RaiseEvent(Envelope.Create(@event));
}
private void VerifyNotCreated()
{
if (Snapshot.SchemaDef != null)
{
throw new DomainException("Schema has already been created.");
}
}
private void VerifyCreatedAndNotDeleted()
{
if (Snapshot.IsDeleted || Snapshot.SchemaDef == null)
{
throw new DomainException("Schema has already been deleted or not created yet.");
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event, registry));
}
}
}

330
src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs

@ -0,0 +1,330 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Guards;
using Squidex.Domain.Apps.Entities.Schemas.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Schemas
{
public sealed class SchemaGrain : DomainObjectGrain<SchemaState>, ISchemaGrain
{
private readonly IAppProvider appProvider;
private readonly FieldRegistry registry;
public SchemaGrain(IStore<Guid> store, IAppProvider appProvider, FieldRegistry registry)
: base(store)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(registry, nameof(registry));
this.appProvider = appProvider;
this.registry = registry;
}
public override Task<object> ExecuteAsync(IAggregateCommand command)
{
switch (command)
{
case CreateSchema createSchema:
return CreateAsync(createSchema, async c =>
{
await GuardSchema.CanCreate(c, appProvider);
Create(c);
});
case AddField addField:
return UpdateReturnAsync(addField, c =>
{
GuardSchemaField.CanAdd(Snapshot.SchemaDef, c);
Add(c);
return EntityCreatedResult.Create(Snapshot.SchemaDef.FieldsById.Values.First(x => x.Name == addField.Name).Id, Version);
});
case DeleteField deleteField:
return UpdateAsync(deleteField, c =>
{
GuardSchemaField.CanDelete(Snapshot.SchemaDef, deleteField);
DeleteField(c);
});
case LockField lockField:
return UpdateAsync(lockField, c =>
{
GuardSchemaField.CanLock(Snapshot.SchemaDef, lockField);
LockField(c);
});
case HideField hideField:
return UpdateAsync(hideField, c =>
{
GuardSchemaField.CanHide(Snapshot.SchemaDef, c);
HideField(c);
});
case ShowField showField:
return UpdateAsync(showField, c =>
{
GuardSchemaField.CanShow(Snapshot.SchemaDef, c);
ShowField(c);
});
case DisableField disableField:
return UpdateAsync(disableField, c =>
{
GuardSchemaField.CanDisable(Snapshot.SchemaDef, c);
DisableField(c);
});
case EnableField enableField:
return UpdateAsync(enableField, c =>
{
GuardSchemaField.CanEnable(Snapshot.SchemaDef, c);
EnableField(c);
});
case UpdateField updateField:
return UpdateAsync(updateField, c =>
{
GuardSchemaField.CanUpdate(Snapshot.SchemaDef, c);
UpdateField(c);
});
case ReorderFields reorderFields:
return UpdateAsync(reorderFields, c =>
{
GuardSchema.CanReorder(Snapshot.SchemaDef, c);
Reorder(c);
});
case UpdateSchema updateSchema:
return UpdateAsync(updateSchema, c =>
{
GuardSchema.CanUpdate(Snapshot.SchemaDef, c);
Update(c);
});
case PublishSchema publishSchema:
return UpdateAsync(publishSchema, c =>
{
GuardSchema.CanPublish(Snapshot.SchemaDef, c);
Publish(c);
});
case UnpublishSchema unpublishSchema:
return UpdateAsync(unpublishSchema, c =>
{
GuardSchema.CanUnpublish(Snapshot.SchemaDef, c);
Unpublish(c);
});
case ConfigureScripts configureScripts:
return UpdateAsync(configureScripts, c =>
{
GuardSchema.CanConfigureScripts(Snapshot.SchemaDef, c);
ConfigureScripts(c);
});
case DeleteSchema deleteSchema:
return UpdateAsync(deleteSchema, c =>
{
GuardSchema.CanDelete(Snapshot.SchemaDef, c);
Delete(c);
});
default:
throw new NotSupportedException();
}
}
public void Create(CreateSchema command)
{
var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId<Guid>(command.SchemaId, command.Name) });
if (command.Fields != null)
{
@event.Fields = new List<SchemaCreatedField>();
foreach (var commandField in command.Fields)
{
var eventField = SimpleMapper.Map(commandField, new SchemaCreatedField());
@event.Fields.Add(eventField);
}
}
RaiseEvent(@event);
}
public void Add(AddField command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) }));
}
public void UpdateField(UpdateField command)
{
VerifyNotDeleted();
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated()));
}
public void LockField(LockField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldLocked());
}
public void HideField(HideField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldHidden());
}
public void ShowField(ShowField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldShown());
}
public void DisableField(DisableField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldDisabled());
}
public void EnableField(EnableField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldEnabled());
}
public void DeleteField(DeleteField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldDeleted());
}
public void Reorder(ReorderFields command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered()));
}
public void Publish(PublishSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished()));
}
public void Unpublish(UnpublishSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished()));
}
public void ConfigureScripts(ConfigureScripts command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
}
public void Delete(DeleteSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted()));
}
public void Update(UpdateSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated()));
}
private void RaiseEvent(FieldCommand fieldCommand, FieldEvent @event)
{
SimpleMapper.Map(fieldCommand, @event);
if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field))
{
@event.FieldId = new NamedId<long>(field.Id, field.Name);
}
RaiseEvent(@event);
}
private void RaiseEvent(SchemaEvent @event)
{
if (@event.SchemaId == null)
{
@event.SchemaId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name);
}
if (@event.AppId == null)
{
@event.AppId = Snapshot.AppId;
}
RaiseEvent(Envelope.Create(@event));
}
private void VerifyNotDeleted()
{
if (Snapshot.IsDeleted)
{
throw new DomainException("Schema has already been deleted.");
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event, registry));
}
}
}

3
src/Squidex.Infrastructure/Assets/AssetFile.cs

@ -7,6 +7,7 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Assets
{
@ -20,11 +21,11 @@ namespace Squidex.Infrastructure.Assets
public long FileSize { get; }
[JsonConstructor]
public AssetFile(string fileName, string mimeType, long fileSize, Func<Stream> openAction)
{
Guard.NotNullOrEmpty(fileName, nameof(fileName));
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
Guard.NotNull(openAction, nameof(openAction));
Guard.GreaterEquals(fileSize, 0, nameof(fileSize));
FileName = fileName;

204
src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs

@ -0,0 +1,204 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Orleans;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
namespace Squidex.Infrastructure.Commands
{
public abstract class DomainObjectGrain<T> : Grain, IDomainObjectGrain where T : IDomainState, new()
{
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>();
private readonly IStore<Guid> store;
private T snapshot = new T { Version = EtagVersion.Empty };
private IPersistence<T> persistence;
public long Version
{
get { return snapshot.Version; }
}
public long NewVersion
{
get { return snapshot.Version + uncomittedEvents.Count; }
}
protected T Snapshot
{
get { return snapshot; }
}
protected DomainObjectGrain(IStore<Guid> store)
{
Guard.NotNull(store, nameof(store));
this.store = store;
}
public override Task OnActivateAsync()
{
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), this.GetPrimaryKey(), ApplySnapshot, ApplyEvent);
return persistence.ReadAsync();
}
public void RaiseEvent(IEvent @event)
{
RaiseEvent(Envelope.Create(@event));
}
public virtual void RaiseEvent(Envelope<IEvent> @event)
{
Guard.NotNull(@event, nameof(@event));
@event.SetAggregateId(this.GetPrimaryKey());
ApplyEvent(@event);
uncomittedEvents.Add(@event);
}
public IReadOnlyList<Envelope<IEvent>> GetUncomittedEvents()
{
return uncomittedEvents;
}
public void ClearUncommittedEvents()
{
uncomittedEvents.Clear();
}
public virtual void ApplySnapshot(T newSnapshot)
{
snapshot = newSnapshot;
}
public virtual void ApplyEvent(Envelope<IEvent> @event)
{
}
public Task WriteSnapshotAsync()
{
snapshot.Version = persistence.Version;
return persistence.WriteSnapshotAsync(snapshot);
}
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler, false);
}
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, x => Task.FromResult(handler(x)), false);
}
protected Task<object> CreateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, x => handler(x).ContinueWith<object>(t => null), false);
}
protected Task<object> CreateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, x => { handler(x); return Task.FromResult<object>(null); }, false);
}
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler, true);
}
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, x => Task.FromResult(handler(x)), true);
}
protected Task<object> UpdateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, x => handler(x).ContinueWith<object>(t => null), true);
}
protected Task<object> UpdateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, x => { handler(x); return Task.FromResult<object>(null); }, true);
}
private async Task<object> InvokeAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler, bool isUpdate) where TCommand : class, IAggregateCommand
{
Guard.NotNull(command, nameof(command));
if (command.ExpectedVersion != EtagVersion.Any && command.ExpectedVersion != Version)
{
throw new DomainObjectVersionException(this.GetPrimaryKey().ToString(), GetType(), Version, command.ExpectedVersion);
}
if (isUpdate && Version < 0)
{
DeactivateOnIdle();
throw new DomainObjectNotFoundException(this.GetPrimaryKey().ToString(), GetType());
}
else if (!isUpdate && Version >= 0)
{
throw new DomainException("Object has already been created.");
}
var previousSnapshot = snapshot;
try
{
var result = await handler(command);
var events = uncomittedEvents.ToArray();
if (events.Length > 0)
{
snapshot.Version = NewVersion;
await persistence.WriteEventsAsync(events);
await persistence.WriteSnapshotAsync(snapshot);
}
if (result == null)
{
if (isUpdate)
{
result = new EntitySavedResult(Version);
}
else
{
result = EntityCreatedResult.Create(this.GetPrimaryKey(), Version);
}
}
return result;
}
catch
{
snapshot = previousSnapshot;
throw;
}
finally
{
uncomittedEvents.Clear();
}
}
public Task<J<object>> ExecuteAsync(J<IAggregateCommand> command)
{
return ExecuteAsync(command.Value).ContinueWith(x => x.Result.AsJ());
}
public abstract Task<object> ExecuteAsync(IAggregateCommand command);
}
}

46
src/Squidex.Infrastructure/Commands/GrainCommandMiddleware.cs

@ -0,0 +1,46 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Orleans;
namespace Squidex.Infrastructure.Commands
{
public class GrainCommandMiddleware<TCommand, TGrain> : ICommandMiddleware where TCommand : IAggregateCommand where TGrain : IDomainObjectGrain
{
private readonly IGrainFactory grainFactory;
public GrainCommandMiddleware(IGrainFactory grainFactory)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
this.grainFactory = grainFactory;
}
public async virtual Task HandleAsync(CommandContext context, Func<Task> next)
{
if (context.Command is TCommand typedCommand)
{
var result = await ExecuteCommandAsync(typedCommand);
context.Complete(result);
}
await next();
}
protected async Task<object> ExecuteCommandAsync(TCommand typedCommand)
{
var grain = grainFactory.GetGrain<TGrain>(typedCommand.AggregateId);
var result = await grain.ExecuteAsync(typedCommand);
return result.Value;
}
}
}

20
src/Squidex.Infrastructure/Commands/IDomainObjectGrain.cs

@ -0,0 +1,20 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Orleans;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Infrastructure.Commands
{
public interface IDomainObjectGrain : IGrainWithGuidKey
{
Task WriteSnapshotAsync();
Task<J<object>> ExecuteAsync(J<IAggregateCommand> command);
}
}

18
src/Squidex.Infrastructure/Orleans/J.cs

@ -0,0 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
#pragma warning disable SA1401 // Fields must be private
namespace Squidex.Infrastructure.Orleans
{
public static class J
{
public static JsonSerializer Serializer = new JsonSerializer();
}
}

17
src/Squidex.Infrastructure/Orleans/JExtensions.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.Orleans
{
public static class JExtensions
{
public static J<T> AsJ<T>(this T value)
{
return new J<T>(value);
}
}
}

85
src/Squidex.Infrastructure/Orleans/J{T}.cs

@ -0,0 +1,85 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Orleans.CodeGeneration;
using Orleans.Serialization;
namespace Squidex.Infrastructure.Orleans
{
public struct J<T>
{
private readonly T value;
public T Value
{
get { return value; }
}
[JsonConstructor]
public J(T value)
{
this.value = value;
}
public static implicit operator T(J<T> value)
{
return value.value;
}
public static implicit operator J<T>(T d)
{
return new J<T>(d);
}
public static Task<J<T>> AsTask(T value)
{
return Task.FromResult<J<T>>(value);
}
[CopierMethod]
private static object Copy(object input, ICopyContext context)
{
return input;
}
[SerializerMethod]
private static void Serialize(object input, ISerializationContext context, Type expected)
{
var stream = new MemoryStream();
using (var writer = new JsonTextWriter(new StreamWriter(stream)))
{
J.Serializer.Serialize(writer, input);
writer.Flush();
}
var outBytes = stream.ToArray();
context.StreamWriter.Write(outBytes.Length);
context.StreamWriter.Write(outBytes);
}
[DeserializerMethod]
private static object Deserialize(Type expected, IDeserializationContext context)
{
var outLength = context.StreamReader.ReadInt();
var outBytes = context.StreamReader.ReadBytes(outLength);
var stream = new MemoryStream(outBytes);
using (var reader = new JsonTextReader(new StreamReader(stream)))
{
return J.Serializer.Deserialize(reader, expected);
}
}
}
}

34
src/Squidex/Config/Domain/SerializationServices.cs

@ -27,15 +27,17 @@ namespace Squidex.Config.Domain
public static class SerializationServices
{
private static readonly TypeNameRegistry TypeNameRegistry =
new TypeNameRegistry()
.MapUnmapped(SquidexCoreModel.Assembly)
.MapUnmapped(SquidexEvents.Assembly)
.MapUnmapped(SquidexInfrastructure.Assembly)
.MapUnmapped(SquidexMigrations.Assembly);
new TypeNameRegistry()
.MapUnmapped(SquidexCoreModel.Assembly)
.MapUnmapped(SquidexEvents.Assembly)
.MapUnmapped(SquidexInfrastructure.Assembly)
.MapUnmapped(SquidexMigrations.Assembly);
private static readonly FieldRegistry FieldRegistry = new FieldRegistry(TypeNameRegistry);
private static JsonSerializerSettings ConfigureJson(JsonSerializerSettings settings, TypeNameHandling typeNameHandling)
public static readonly JsonSerializerSettings DefaultJsonSettings = new JsonSerializerSettings();
public static readonly JsonSerializer DefaultJsonSerializer;
private static void ConfigureJson(JsonSerializerSettings settings, TypeNameHandling typeNameHandling)
{
settings.SerializationBinder = new TypeNameSerializationBinder(TypeNameRegistry);
@ -65,22 +67,24 @@ namespace Squidex.Config.Domain
settings.TypeNameHandling = typeNameHandling;
settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
}
static SerializationServices()
{
ConfigureJson(DefaultJsonSettings, TypeNameHandling.Auto);
DefaultJsonSerializer = JsonSerializer.Create(DefaultJsonSettings);
return settings;
BsonJsonConvention.Register(DefaultJsonSerializer);
}
public static IServiceCollection AddMySerializers(this IServiceCollection services)
{
var serializerSettings = ConfigureJson(new JsonSerializerSettings(), TypeNameHandling.Auto);
var serializerInstance = JsonSerializer.Create(serializerSettings);
services.AddSingletonAs(t => FieldRegistry);
services.AddSingletonAs(t => serializerSettings);
services.AddSingletonAs(t => serializerInstance);
services.AddSingletonAs(t => DefaultJsonSettings);
services.AddSingletonAs(t => DefaultJsonSerializer);
services.AddSingletonAs(t => TypeNameRegistry);
BsonJsonConvention.Register(serializerInstance);
return services;
}

17
src/Squidex/Config/Domain/WriteServices.cs

@ -12,11 +12,13 @@ using Migrate_01;
using Migrate_01.Migrations;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Templates;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Users;
using Squidex.Infrastructure.Commands;
@ -59,10 +61,10 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<ContentCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<SchemaCommandMiddleware>()
services.AddSingletonAs<GrainCommandMiddleware<SchemaCommand, ISchemaGrain>>()
.As<ICommandMiddleware>();
services.AddSingletonAs<RuleCommandMiddleware>()
services.AddSingletonAs<GrainCommandMiddleware<RuleCommand, IRuleGrain>>()
.As<ICommandMiddleware>();
services.AddSingletonAs<CreateBlogCommandMiddleware>()
@ -92,19 +94,10 @@ namespace Squidex.Config.Domain
services.AddTransientAs<AppDomainObject>()
.AsSelf();
services.AddTransientAs<AssetDomainObject>()
.AsSelf();
services.AddTransientAs<ContentDomainObject>()
.AsSelf();
services.AddTransientAs<RuleDomainObject>()
.AsSelf();
services.AddTransientAs<SchemaDomainObject>()
.AsSelf();
services.AddSingleton<InitialPatterns>(c =>
services.AddSingleton(c =>
{
var config = c.GetRequiredService<IOptions<MyUIOptions>>();

4
src/Squidex/Config/Orleans/SiloWrapper.cs

@ -11,9 +11,11 @@ using Microsoft.Extensions.Configuration;
using Orleans;
using Orleans.Hosting;
using Orleans.Runtime.Configuration;
using Squidex.Config.Domain;
using Squidex.Domain.Apps.Entities;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log.Adapter;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Config.Orleans
{
@ -38,6 +40,8 @@ namespace Squidex.Config.Orleans
public SiloWrapper(IConfiguration configuration)
{
J.Serializer = SerializationServices.DefaultJsonSerializer;
silo = SiloHostBuilder.CreateDefault()
.UseConfiguration(ClusterConfiguration.LocalhostPrimarySilo(33333).WithDashboard())
.UseContentRoot(Directory.GetCurrentDirectory())

Loading…
Cancel
Save