diff --git a/src/Microsoft.Restier.Core/ApiBase.cs b/src/Microsoft.Restier.Core/ApiBase.cs
index 5c886877..c7c23307 100644
--- a/src/Microsoft.Restier.Core/ApiBase.cs
+++ b/src/Microsoft.Restier.Core/ApiBase.cs
@@ -3,7 +3,10 @@
using System;
using System.Collections.Concurrent;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Restier.Core.Submit;
namespace Microsoft.Restier.Core
{
@@ -12,15 +15,16 @@ namespace Microsoft.Restier.Core
///
///
///
- /// An API configuration is intended to be long-lived, and can be
- /// statically cached according to an API type specified when the
- /// configuration is created. Additionally, the API model produced
- /// as a result of a particular configuration is cached under the same
+ /// An API configuration is intended to be long-lived, and can be statically cached according to an API type specified when the
+ /// configuration is created. Additionally, the API model produced as a result of a particular configuration is cached under the same
/// API type to avoid re-computing it on each invocation.
///
///
public abstract class ApiBase : IDisposable
{
+
+ #region Private Members
+
private static ConcurrentDictionary> publisherServicesCallback =
new ConcurrentDictionary>();
@@ -28,19 +32,21 @@ public abstract class ApiBase : IDisposable
private ApiConfiguration apiConfiguration;
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// An containing all services of this .
- ///
- protected ApiBase(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider;
+ private readonly DefaultSubmitHandler submitHandler;
+
+ #endregion
+
+ #region Public Properties
///
/// Gets the which contains all services of this .
///
public IServiceProvider ServiceProvider { get; private set; }
+ #endregion
+
+ #region Internal Properties
+
///
/// Gets the API configuration for this API.
///
@@ -57,6 +63,26 @@ internal ApiConfiguration Configuration
}
}
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// An containing all services of this .
+ ///
+ protected ApiBase(IServiceProvider serviceProvider)
+ {
+ ServiceProvider = serviceProvider;
+ submitHandler = new DefaultSubmitHandler(serviceProvider);
+ }
+
+ #endregion
+
+ #region Static Methods
+
///
/// Configure services for this API.
///
@@ -99,9 +125,7 @@ public static void AddPublisherServices(Type apiType, Action
///
/// Get publisher registering service callback for specified Api.
///
- ///
- /// The Api type of which to get the publisher registering service callback.
- ///
+ /// The Api type of which to get the publisher registering service callback.
/// The service registering callback.
//[CLSCompliant(false)]
public static Action GetPublisherServiceCallback(Type apiType)
@@ -114,6 +138,26 @@ public static Action GetPublisherServiceCallback(Type apiTyp
return emptyConfig;
}
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Asynchronously submits changes made using an API context.
+ ///
+ /// A change set, or null to submit existing pending changes.
+ /// An optional cancellation token.
+ /// A task that represents the asynchronous operation whose result is a submit result.
+ public async Task SubmitAsync(ChangeSet changeSet = null, CancellationToken cancellationToken = default)
+ {
+ var submitContext = new SubmitContext(ServiceProvider, changeSet);
+ return await submitHandler.SubmitAsync(submitContext, cancellationToken).ConfigureAwait(false);
+ }
+
+ #endregion
+
+ #region IDisposable Pattern
+
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
@@ -140,6 +184,8 @@ protected virtual void Dispose(bool disposing)
}
}
+ #endregion
+
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Restier.Core/ApiConfiguration.cs b/src/Microsoft.Restier.Core/ApiConfiguration.cs
index b7ea64f2..f7d5b984 100644
--- a/src/Microsoft.Restier.Core/ApiConfiguration.cs
+++ b/src/Microsoft.Restier.Core/ApiConfiguration.cs
@@ -26,7 +26,7 @@ internal class ApiConfiguration
internal IEdmModel Model { get; private set; }
- internal TaskCompletionSource CompeteModelGeneration(out Task running)
+ internal TaskCompletionSource CompleteModelGeneration(out Task running)
{
var source = new TaskCompletionSource(TaskCreationOptions.AttachedToParent);
var runningTask = Interlocked.CompareExchange(ref modelTask, source.Task, null);
diff --git a/src/Microsoft.Restier.Core/Exceptions/ChangeSetValidationException.cs b/src/Microsoft.Restier.Core/Exceptions/ChangeSetValidationException.cs
index a5fa80c8..d6f126b2 100644
--- a/src/Microsoft.Restier.Core/Exceptions/ChangeSetValidationException.cs
+++ b/src/Microsoft.Restier.Core/Exceptions/ChangeSetValidationException.cs
@@ -14,6 +14,9 @@ public class ChangeSetValidationException : Exception
{
private IEnumerable errorValidationResults;
+ ///
+ ///
+ ///
public ChangeSetValidationException()
{
}
diff --git a/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs b/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs
index c82b4cdf..4d37a0e4 100644
--- a/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs
+++ b/src/Microsoft.Restier.Core/Extensions/ApiBaseExtensions.cs
@@ -177,7 +177,7 @@ public static IEnumerable GetApiServices(this ApiBase api) where T : class
throw new InvalidOperationException(Resources.ModelBuilderNotRegistered);
}
- var source = config.CompeteModelGeneration(out var running);
+ var source = config.CompleteModelGeneration(out var running);
if (source == null)
{
return await running.ConfigureAwait(false);
@@ -403,34 +403,6 @@ public static IQueryable GetQueryableSource(this ApiBase api
#endregion
- #region Submit
-
- ///
- /// Asynchronously submits changes made using an API context.
- ///
- ///
- /// An API.
- ///
- ///
- /// A change set, or null to submit existing pending changes.
- ///
- ///
- /// An optional cancellation token.
- ///
- ///
- /// A task that represents the asynchronous
- /// operation whose result is a submit result.
- ///
- public static async Task SubmitAsync(this ApiBase api, ChangeSet changeSet = null, CancellationToken cancellationToken = default(CancellationToken))
- {
- Ensure.NotNull(api, nameof(api));
-
- var submitContext = new SubmitContext(api.ServiceProvider, changeSet);
- return await DefaultSubmitHandler.SubmitAsync(submitContext, cancellationToken).ConfigureAwait(false);
- }
-
- #endregion
-
#region GetQueryableSource Private
///
diff --git a/src/Microsoft.Restier.Core/Extensions/ServiceCollectionExtensions.cs b/src/Microsoft.Restier.Core/Extensions/ServiceCollectionExtensions.cs
index 58d9acde..32b39fb6 100644
--- a/src/Microsoft.Restier.Core/Extensions/ServiceCollectionExtensions.cs
+++ b/src/Microsoft.Restier.Core/Extensions/ServiceCollectionExtensions.cs
@@ -1,14 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Restier.Core.Query;
-using Microsoft.Restier.Core.Submit;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Restier.Core.Query;
+using Microsoft.Restier.Core.Submit;
namespace Microsoft.Restier.Core
{
diff --git a/src/Microsoft.Restier.Core/Submit/DefaultSubmitHandler.cs b/src/Microsoft.Restier.Core/Submit/DefaultSubmitHandler.cs
index 48b3d7d4..e3fd0248 100644
--- a/src/Microsoft.Restier.Core/Submit/DefaultSubmitHandler.cs
+++ b/src/Microsoft.Restier.Core/Submit/DefaultSubmitHandler.cs
@@ -10,14 +10,47 @@
using System.Security;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Restier.Core.Submit
{
///
- /// Represents the default submit handler.
+ /// The default handler for submitting changes through the .
///
- internal static class DefaultSubmitHandler
+ internal class DefaultSubmitHandler
{
+
+ #region Private Members
+
+ private readonly IChangeSetInitializer initializer;
+ private readonly IChangeSetItemAuthorizer authorizer;
+ private readonly IChangeSetItemValidator validator;
+ private readonly IChangeSetItemFilter filter;
+ private readonly ISubmitExecutor executor;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ ///
+ ///
+ ///
+ public DefaultSubmitHandler(IServiceProvider serviceProvider)
+ {
+ //RWM: This stuff SHOULD be getting passed into a constructor. But the DI implementation is less than awesome.
+ // So we'll work around it for now and still save some allocations.
+ initializer = serviceProvider.GetService();
+ executor = serviceProvider.GetService();
+ authorizer = serviceProvider.GetService();
+ validator = serviceProvider.GetService();
+ filter = serviceProvider.GetService();
+ }
+
+ #endregion
+
+ #region Public Methods
+
///
/// Asynchronously executes the submit flow.
///
@@ -31,17 +64,16 @@ internal static class DefaultSubmitHandler
/// A task that represents the asynchronous
/// operation whose result is a submit result.
///
- public static async Task SubmitAsync(SubmitContext context, CancellationToken cancellationToken)
+ public async Task SubmitAsync(SubmitContext context, CancellationToken cancellationToken)
{
Ensure.NotNull(context, nameof(context));
- var preparer = context.GetApiService();
- if (preparer == null)
+ if (initializer == null)
{
throw new NotSupportedException(Resources.ChangeSetPreparerMissing);
}
- await preparer.InitializeAsync(context, cancellationToken).ConfigureAwait(false);
+ await initializer.InitializeAsync(context, cancellationToken).ConfigureAwait(false);
if (context.Result != null)
{
@@ -65,6 +97,10 @@ public static async Task SubmitAsync(SubmitContext context, Cancel
return context.Result;
}
+ #endregion
+
+ #region Private Methods
+
private static string GetAuthorizeFailedMessage(ChangeSetItem item)
{
switch (item.Type)
@@ -96,7 +132,7 @@ private static string GetAuthorizeFailedMessage(ChangeSetItem item)
}
}
- private static async Task PerformValidate(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
+ private async Task PerformValidate(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
{
await InvokeAuthorizers(context, changeSetItems, cancellationToken).ConfigureAwait(false);
@@ -115,9 +151,8 @@ private static async Task PerformValidate(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
+ private async Task InvokeAuthorizers(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
{
- var authorizer = context.GetApiService();
if (authorizer == null)
{
return;
@@ -132,9 +167,8 @@ private static async Task InvokeAuthorizers(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
+ private async Task InvokeValidators(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
{
- var validator = context.GetApiService();
if (validator == null)
{
return;
@@ -159,11 +193,12 @@ private static async Task InvokeValidators(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
+ private async Task PerformPreEvent(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
{
-
- var processor = context.GetApiService();
- //TODO: Should we error out if there is no ChangeSetFilter? No consistent pattern to follow.
+ if (filter == null)
+ {
+ return;
+ }
foreach (var item in changeSetItems)
{
@@ -172,9 +207,9 @@ private static async Task PerformPreEvent(SubmitContext context, IEnumerable();
if (executor == null)
{
throw new NotSupportedException(Resources.SubmitExecutorMissing);
@@ -204,19 +238,21 @@ private static async Task PerformPersist(SubmitContext context, CancellationToke
context.Result = await executor.ExecuteSubmitAsync(context, cancellationToken).ConfigureAwait(false);
}
- private static async Task PerformPostEvent(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
+ private async Task PerformPostEvent(SubmitContext context, IEnumerable changeSetItems, CancellationToken cancellationToken)
{
- var processor = context.GetApiService();
- if (processor == null)
+ if (filter == null)
{
- //TODO: It feels like we should be following the same pattern as teh method above.
return;
}
foreach (var item in changeSetItems)
{
- await processor.OnChangeSetItemProcessedAsync(context, item, cancellationToken).ConfigureAwait(false);
+ await filter.OnChangeSetItemProcessedAsync(context, item, cancellationToken).ConfigureAwait(false);
}
}
+
+ #endregion
+
}
-}
+
+}
\ No newline at end of file