diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs
index cc2415f4cd..0d354b65de 100644
--- a/src/Umbraco.Core/Deploy/ArtifactBase.cs
+++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs
@@ -1,50 +1,49 @@
-namespace Umbraco.Cms.Core.Deploy
+namespace Umbraco.Cms.Core.Deploy;
+
+///
+/// Provides a base class to all artifacts.
+///
+public abstract class ArtifactBase : IArtifact
+ where TUdi : Udi
{
- ///
- /// Provides a base class to all artifacts.
- ///
- public abstract class ArtifactBase : IArtifact
- where TUdi : Udi
+ protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null)
{
- protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null)
- {
- Udi = udi ?? throw new ArgumentNullException("udi");
- Name = Udi.ToString();
+ Udi = udi ?? throw new ArgumentNullException("udi");
+ Name = Udi.ToString();
- _dependencies = dependencies ?? Enumerable.Empty();
- _checksum = new Lazy(GetChecksum);
- }
-
- private readonly Lazy _checksum;
-
- private IEnumerable _dependencies;
-
- protected abstract string GetChecksum();
-
- Udi IArtifactSignature.Udi => Udi;
-
- public TUdi Udi { get; set; }
-
- public string Checksum => _checksum.Value;
-
- ///
- /// Prevents the property from being serialized.
- ///
- ///
- /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore]
- /// as that would require an external dependency in Umbraco.Cms.Core.
- /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
- ///
- public bool ShouldSerializeChecksum() => false;
-
- public IEnumerable Dependencies
- {
- get => _dependencies;
- set => _dependencies = value.OrderBy(x => x.Udi);
- }
-
- public string Name { get; set; }
-
- public string Alias { get; set; } = string.Empty;
+ _dependencies = dependencies ?? Enumerable.Empty();
+ _checksum = new Lazy(GetChecksum);
}
+
+ private readonly Lazy _checksum;
+
+ private IEnumerable _dependencies;
+
+ protected abstract string GetChecksum();
+
+ Udi IArtifactSignature.Udi => Udi;
+
+ public TUdi Udi { get; set; }
+
+ public string Checksum => _checksum.Value;
+
+ ///
+ /// Prevents the property from being serialized.
+ ///
+ ///
+ /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore]
+ /// as that would require an external dependency in Umbraco.Cms.Core.
+ /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
+ ///
+ public bool ShouldSerializeChecksum() => false;
+
+ public IEnumerable Dependencies
+ {
+ get => _dependencies;
+ set => _dependencies = value.OrderBy(x => x.Udi);
+ }
+
+ public string Name { get; set; }
+
+ public string Alias { get; set; } = string.Empty;
}
diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs
index 1b75fe11c0..e2dd343af1 100644
--- a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs
+++ b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs
@@ -44,3 +44,40 @@ public abstract class ArtifactDeployState
///
protected abstract IArtifact GetArtifactAsIArtifact();
}
+
+///
+/// Represent the state of an artifact being deployed.
+///
+/// The type of the artifact.
+/// The type of the entity.
+public class ArtifactDeployState : ArtifactDeployState
+ where TArtifact : IArtifact
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The artifact.
+ /// The entity.
+ /// The service connector deploying the artifact.
+ /// The next pass number.
+ public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass)
+ {
+ Artifact = art;
+ Entity = entity;
+ Connector = connector;
+ NextPass = nextPass;
+ }
+
+ ///
+ /// Gets or sets the artifact.
+ ///
+ public new TArtifact Artifact { get; set; }
+
+ ///
+ /// Gets or sets the entity.
+ ///
+ public TEntity? Entity { get; set; }
+
+ ///
+ protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact;
+}
diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs
deleted file mode 100644
index 0ff1e20e87..0000000000
--- a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace Umbraco.Cms.Core.Deploy;
-
-///
-/// Represent the state of an artifact being deployed.
-///
-/// The type of the artifact.
-/// The type of the entity.
-public class ArtifactDeployState : ArtifactDeployState
- where TArtifact : IArtifact
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The artifact.
- /// The entity.
- /// The service connector deploying the artifact.
- /// The next pass number.
- public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass)
- {
- Artifact = art;
- Entity = entity;
- Connector = connector;
- NextPass = nextPass;
- }
-
- ///
- /// Gets or sets the artifact.
- ///
- public new TArtifact Artifact { get; set; }
-
- ///
- /// Gets or sets the entity.
- ///
- public TEntity? Entity { get; set; }
-
- ///
- protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact;
-}
diff --git a/src/Umbraco.Core/Deploy/IContextCache.cs b/src/Umbraco.Core/Deploy/IContextCache.cs
new file mode 100644
index 0000000000..d175064905
--- /dev/null
+++ b/src/Umbraco.Core/Deploy/IContextCache.cs
@@ -0,0 +1,31 @@
+namespace Umbraco.Cms.Core.Deploy;
+
+///
+/// Represents a context cache used by Deploy operations.
+///
+public interface IContextCache
+{
+ ///
+ /// Creates the item on the context cache using the specified .
+ ///
+ /// The type of the cached item.
+ /// The key of the cached item.
+ /// The item.
+ void Create(string key, T item);
+
+ ///
+ /// Gets an item from the context cache or creates and stores it using the specified .
+ ///
+ /// The type of the cached item.
+ /// The key of the cached item.
+ /// The factory method to create the item (if it doesn't exist yet).
+ ///
+ /// The item.
+ ///
+ T? GetOrCreate(string key, Func factory);
+
+ ///
+ /// Clears all cached items on this context.
+ ///
+ void Clear();
+}
diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs
index 6b91926b57..506d1f5745 100644
--- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs
+++ b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs
@@ -1,37 +1,67 @@
-using System.Diagnostics.CodeAnalysis;
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Deploy;
///
-/// Defines methods that can convert data type configuration to / from an environment-agnostic string.
+/// Defines methods that can convert data type configuration to / from an environment-agnostic string.
///
///
-/// Configuration may contain values such as content identifiers, that would be local
-/// to one environment, and need to be converted in order to be deployed.
+/// Configuration may contain values such as content identifiers, that would be local
+/// to one environment, and need to be converted in order to be deployed.
///
-[SuppressMessage(
- "ReSharper",
- "UnusedMember.Global",
- Justification = "This is actual only used by Deploy, but we don't want third parties to have references on deploy, that's why this interface is part of core.")]
public interface IDataTypeConfigurationConnector
{
///
- /// Gets the property editor aliases that the value converter supports by default.
+ /// Gets the property editor aliases that the value converter supports by default.
///
+ ///
+ /// The property editor aliases.
+ ///
IEnumerable PropertyEditorAliases { get; }
///
- /// Gets the artifact datatype configuration corresponding to the actual datatype configuration.
+ /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies.
///
- /// The datatype.
+ /// The data type.
/// The dependencies.
- string? ToArtifact(IDataType dataType, ICollection dependencies);
+ ///
+ /// The artifact configuration value.
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ string? ToArtifact(IDataType dataType, ICollection dependencies)
+ => ToArtifact(dataType, dependencies, PassThroughCache.Instance);
///
- /// Gets the actual datatype configuration corresponding to the artifact configuration.
+ /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies.
///
- /// The datatype.
- /// The artifact configuration.
- object? FromArtifact(IDataType dataType, string? configuration);
+ /// The data type.
+ /// The dependencies.
+ /// The context cache.
+ ///
+ /// The artifact configuration value.
+ ///
+ string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache);
+
+ ///
+ /// Gets the data type configuration corresponding to an artifact configuration value.
+ ///
+ /// The data type.
+ /// The artifact configuration value.
+ ///
+ /// The data type configuration.
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ object? FromArtifact(IDataType dataType, string? configuration)
+ => FromArtifact(dataType, configuration, PassThroughCache.Instance);
+
+ ///
+ /// Gets the data type configuration corresponding to an artifact configuration value.
+ ///
+ /// The data type.
+ /// The artifact configuration value.
+ /// The context cache.
+ ///
+ /// The data type configuration.
+ ///
+ object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache);
}
diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs
index f6cd7c8002..b4f530fb35 100644
--- a/src/Umbraco.Core/Deploy/IServiceConnector.cs
+++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs
@@ -3,34 +3,65 @@ using Umbraco.Cms.Core.Composing;
namespace Umbraco.Cms.Core.Deploy;
///
-/// Connects to an Umbraco service.
+/// Connects to an Umbraco service.
///
+///
public interface IServiceConnector : IDiscoverable
{
///
- /// Gets an artifact.
+ /// Gets an artifact.
///
/// The entity identifier of the artifact.
- /// The corresponding artifact, or null.
- IArtifact? GetArtifact(Udi udi);
+ ///
+ /// The corresponding artifact, or null.
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ IArtifact? GetArtifact(Udi udi)
+ => GetArtifact(udi, PassThroughCache.Instance);
///
- /// Gets an artifact.
+ /// Gets an artifact.
+ ///
+ /// The entity identifier of the artifact.
+ /// The context cache.
+ ///
+ /// The corresponding artifact, or null.
+ ///
+ IArtifact? GetArtifact(Udi udi, IContextCache contextCache);
+
+ ///
+ /// Gets an artifact.
///
/// The entity.
- /// The corresponding artifact.
- IArtifact GetArtifact(object entity);
+ ///
+ /// The corresponding artifact.
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ IArtifact GetArtifact(object entity)
+ => GetArtifact(entity, PassThroughCache.Instance);
///
- /// Initializes processing for an artifact.
+ /// Gets an artifact.
+ ///
+ /// The entity.
+ /// The context cache.
+ ///
+ /// The corresponding artifact.
+ ///
+ IArtifact GetArtifact(object entity, IContextCache contextCache);
+
+ ///
+ /// Initializes processing for an artifact.
///
/// The artifact.
/// The deploy context.
- /// The mapped artifact.
+ ///
+ /// The mapped artifact.
+ ///
ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context);
///
- /// Processes an artifact.
+ /// Processes an artifact.
///
/// The mapped artifact.
/// The deploy context.
@@ -38,46 +69,56 @@ public interface IServiceConnector : IDiscoverable
void Process(ArtifactDeployState dart, IDeployContext context, int pass);
///
- /// Explodes a range into udis.
+ /// Explodes a range into udis.
///
/// The range.
/// The list of udis where to add the new udis.
- /// Also, it's cool to have a method named Explode. Kaboom!
+ ///
+ /// Also, it's cool to have a method named Explode. Kaboom!
+ ///
void Explode(UdiRange range, List udis);
///
- /// Gets a named range for a specified udi and selector.
+ /// Gets a named range for a specified udi and selector.
///
/// The udi.
/// The selector.
- /// The named range for the specified udi and selector.
+ ///
+ /// The named range for the specified udi and selector.
+ ///
NamedUdiRange GetRange(Udi udi, string selector);
///
- /// Gets a named range for specified entity type, identifier and selector.
+ /// Gets a named range for specified entity type, identifier and selector.
///
/// The entity type.
/// The identifier.
/// The selector.
- /// The named range for the specified entity type, identifier and selector.
+ ///
+ /// The named range for the specified entity type, identifier and selector.
+ ///
///
- /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now?
- ///
- /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do
- /// not manage guids but only ints... so we have to provide a way to support it. The string id here
- /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to
- /// indicate the "root" i.e. an open udi.
- ///
+ /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now?
+ ///
+ /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do
+ /// not manage guids but only ints... so we have to provide a way to support it. The string id here
+ /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to
+ /// indicate the "root" i.e. an open udi.
+ ///
///
NamedUdiRange GetRange(string entityType, string sid, string selector);
///
- /// Compares two artifacts.
+ /// Compares two artifacts.
///
/// The first artifact.
/// The second artifact.
/// A collection of differences to append to, if not null.
- /// A boolean value indicating whether the artifacts are identical.
- /// ServiceConnectorBase{TArtifact} provides a very basic default implementation.
+ ///
+ /// A boolean value indicating whether the artifacts are identical.
+ ///
+ ///
+ /// ServiceConnectorBase{TArtifact} provides a very basic default implementation.
+ ///
bool Compare(IArtifact? art1, IArtifact? art2, ICollection? differences = null);
}
diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs
index f2a776c7ca..9f3b17f71c 100644
--- a/src/Umbraco.Core/Deploy/IValueConnector.cs
+++ b/src/Umbraco.Core/Deploy/IValueConnector.cs
@@ -3,35 +3,70 @@ using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Deploy;
///
-/// Defines methods that can convert a property value to / from an environment-agnostic string.
+/// Defines methods that can convert a property value to and from an environment-agnostic string.
///
///
-/// Property values may contain values such as content identifiers, that would be local
-/// to one environment, and need to be converted in order to be deployed. Connectors also deal
-/// with serializing to / from string.
+/// Property values may contain values such as content identifiers, that would be local
+/// to one environment and need to be converted in order to be deployed. Connectors also deal
+/// with serializing and deserializing the content value to an environment-agnostic string.
///
public interface IValueConnector
{
///
- /// Gets the property editor aliases that the value converter supports by default.
+ /// Gets the property editor aliases that the value converter supports by default.
///
+ ///
+ /// The property editor aliases.
+ ///
IEnumerable PropertyEditorAliases { get; }
///
- /// Gets the deploy property value corresponding to a content property value, and gather dependencies.
+ /// Gets the deploy property value corresponding to a content property value, and gather dependencies.
///
/// The content property value.
/// The value property type
/// The content dependencies.
- /// The deploy property value.
- string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies);
+ ///
+ /// The deploy property value.
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies)
+ => ToArtifact(value, propertyType, dependencies, PassThroughCache.Instance);
///
- /// Gets the content property value corresponding to a deploy property value.
+ /// Gets the deploy property value corresponding to a content property value, and gather dependencies.
+ ///
+ /// The content property value.
+ /// The value property type
+ /// The content dependencies.
+ /// The context cache.
+ ///
+ /// The deploy property value.
+ ///
+ string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache);
+
+ ///
+ /// Gets the content property value corresponding to a deploy property value.
///
/// The deploy property value.
/// The value property type
/// The current content property value.
- /// The content property value.
- object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue);
+ ///
+ /// The content property value.
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue)
+ => FromArtifact(value, propertyType, currentValue, PassThroughCache.Instance);
+
+ ///
+ /// Gets the content property value corresponding to a deploy property value.
+ ///
+ /// The deploy property value.
+ /// The value property type
+ /// The current content property value.
+ /// The context cache.
+ ///
+ /// The content property value.
+ ///
+ object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache);
}
diff --git a/src/Umbraco.Core/Deploy/PassThroughCache.cs b/src/Umbraco.Core/Deploy/PassThroughCache.cs
new file mode 100644
index 0000000000..064fc10ad5
--- /dev/null
+++ b/src/Umbraco.Core/Deploy/PassThroughCache.cs
@@ -0,0 +1,34 @@
+namespace Umbraco.Cms.Core.Deploy;
+
+///
+/// A pass through context cache that always creates the items.
+///
+///
+public sealed class PassThroughCache : IContextCache
+{
+ ///
+ /// Gets the instance.
+ ///
+ ///
+ /// The instance.
+ ///
+ public static PassThroughCache Instance { get; } = new PassThroughCache();
+
+ ///
+ /// Prevents a default instance of the class from being created.
+ ///
+ private PassThroughCache()
+ { }
+
+ ///
+ public void Create(string key, T item)
+ { }
+
+ ///
+ public T? GetOrCreate(string key, Func factory)
+ => factory();
+
+ ///
+ public void Clear()
+ { }
+}
diff --git a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs
index eb1bc518ac..a9ed5fd84d 100644
--- a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs
+++ b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs
@@ -1,44 +1,77 @@
using Umbraco.Cms.Core.Models;
+using static Umbraco.Cms.Core.Models.GridValue;
namespace Umbraco.Cms.Core.Deploy;
///
-/// Defines methods that can convert a grid cell value to / from an environment-agnostic string.
+/// Defines methods that can convert a grid cell value to / from an environment-agnostic string.
///
///
-/// Grid cell values may contain values such as content identifiers, that would be local
-/// to one environment, and need to be converted in order to be deployed.
+/// Grid cell values may contain values such as content identifiers, that would be local
+/// to one environment, and need to be converted in order to be deployed.
///
public interface IGridCellValueConnector
{
///
- /// Gets a value indicating whether the connector supports a specified grid editor view.
+ /// Gets a value indicating whether the connector supports a specified grid editor view.
///
- ///
- /// The grid editor view. It needs to be the view instead of the alias as the view is really what
- /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases
- /// using the same kind of view.
- ///
- /// A value indicating whether the connector supports the grid editor view.
- /// Note that can be string.Empty to indicate the "default" connector.
+ /// The grid editor view. It needs to be the view instead of the alias as the view is really what
+ /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases
+ /// using the same kind of view.
+ ///
+ /// true if the specified view is connector; otherwise, false.
+ ///
+ ///
+ /// A value indicating whether the connector supports the grid editor view.
+ ///
bool IsConnector(string view);
///
- /// Gets the value to be deployed from the control value as a string.
+ /// Gets the value to be deployed from the control value as a string.
///
/// The control containing the value.
/// The dependencies of the property.
- /// The grid cell value to be deployed.
- /// Note that
- string? GetValue(GridValue.GridControl gridControl, ICollection dependencies);
+ ///
+ /// The grid cell value to be deployed.
+ ///
+ ///
+ /// Note that
+ ///
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ string? GetValue(GridValue.GridControl gridControl, ICollection dependencies)
+ => GetValue(gridControl, dependencies, PassThroughCache.Instance);
///
- /// Allows you to modify the value of a control being deployed.
+ /// Gets the value to be deployed from the control value as a string.
+ ///
+ /// The control containing the value.
+ /// The dependencies of the property.
+ /// The context cache.
+ ///
+ /// The grid cell value to be deployed.
+ ///
+ string? GetValue(GridValue.GridControl gridControl, ICollection dependencies, IContextCache contextCache);
+
+ ///
+ /// Allows you to modify the value of a control being deployed.
///
/// The control being deployed.
///
- /// Follows the pattern of the property value connectors (). The SetValue method is
- /// used to modify the value of the .
+ /// Follows the pattern of the property value connectors ().
+ /// The SetValue method is used to modify the value of the .
///
- void SetValue(GridValue.GridControl gridControl);
+ [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
+ void SetValue(GridValue.GridControl gridControl)
+ => SetValue(gridControl, PassThroughCache.Instance);
+
+ ///
+ /// Allows you to modify the value of a control being deployed.
+ ///
+ /// The control being deployed.
+ /// The context cache.
+ ///
+ /// Follows the pattern of the property value connectors ().
+ /// The SetValue method is used to modify the value of the .
+ ///
+ void SetValue(GridValue.GridControl gridControl, IContextCache contextCache);
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs
index 1596bf9888..3cc54f4933 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs
@@ -307,9 +307,9 @@ public class UdiTests
[UdiDefinition("foo", UdiType.GuidUdi)]
public class FooConnector : IServiceConnector
{
- public IArtifact GetArtifact(Udi udi) => throw new NotImplementedException();
+ public IArtifact GetArtifact(Udi udi, IContextCache contextCache) => throw new NotImplementedException();
- public IArtifact GetArtifact(object entity) => throw new NotImplementedException();
+ public IArtifact GetArtifact(object entity, IContextCache contextCache) => throw new NotImplementedException();
public ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context) =>
throw new NotImplementedException();