diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs
index e6b6ec17..b790cae3 100644
--- a/src/GlobalSuppressions.cs
+++ b/src/GlobalSuppressions.cs
@@ -158,6 +158,7 @@
[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Restier.Core.QueryableSource.#System.Linq.IQueryProvider.CreateQuery(System.Linq.Expressions.Expression)")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntitySetTypeMap")]
[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.Restier.Core.Model.ModelContext.#EntityTypeKeyPropertiesMap")]
+[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.Restier.Core.Operation.OperationContext.#ParametersValue")]
#endregion
#region CA1801 Unused Parameters
diff --git a/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemAuthorizer.cs b/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemAuthorizer.cs
index 294f3f11..f5b8ad10 100644
--- a/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemAuthorizer.cs
+++ b/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemAuthorizer.cs
@@ -80,26 +80,21 @@ private static string GetAuthorizeMethodName(ChangeSetItem item)
case ChangeSetItemType.DataModification:
DataModificationItem dataModification = (DataModificationItem)item;
string operationName = null;
- if (dataModification.ChangeSetItemAction == ChangeSetItemAction.Insert)
+ if (dataModification.DataModificationItemAction == DataModificationItemAction.Insert)
{
operationName = ConventionBasedChangeSetConstants.AuthorizeMethodDataModificationInsert;
}
- else if (dataModification.ChangeSetItemAction == ChangeSetItemAction.Update)
+ else if (dataModification.DataModificationItemAction == DataModificationItemAction.Update)
{
operationName = ConventionBasedChangeSetConstants.AuthorizeMethodDataModificationUpdate;
}
- else if (dataModification.ChangeSetItemAction == ChangeSetItemAction.Remove)
+ else if (dataModification.DataModificationItemAction == DataModificationItemAction.Remove)
{
operationName = ConventionBasedChangeSetConstants.AuthorizeMethodDataModificationDelete;
}
return operationName + dataModification.EntitySetName;
- case ChangeSetItemType.ActionInvocation:
- ActionInvocationItem actionItem = (ActionInvocationItem)item;
- return ConventionBasedChangeSetConstants.AuthorizeMethodActionInvocationExecute +
- actionItem.ActionName;
-
default:
throw new InvalidOperationException(string.Format(
CultureInfo.InvariantCulture, Resources.InvalidChangeSetEntryType, item.Type));
diff --git a/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemProcessor.cs b/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemProcessor.cs
index 256d3d74..5f02171f 100644
--- a/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemProcessor.cs
+++ b/src/Microsoft.Restier.Core/Conventions/ConventionBasedChangeSetItemProcessor.cs
@@ -14,7 +14,7 @@
namespace Microsoft.Restier.Core.Conventions
{
///
- /// A convention-based change set item filter.
+ /// A convention-based change set item processor which calls logic like OnInserting and OnInserted.
///
internal class ConventionBasedChangeSetItemProcessor : IChangeSetItemProcessor
{
@@ -43,7 +43,7 @@ public Task OnProcessingChangeSetItemAsync(
ChangeSetItem item,
CancellationToken cancellationToken)
{
- return this.InvokeFilterMethodAsync(
+ return this.InvokeProcessorMethodAsync(
context, item, ConventionBasedChangeSetConstants.FilterMethodNamePreFilterSuffix);
}
@@ -53,7 +53,7 @@ public Task OnProcessedChangeSetItemAsync(
ChangeSetItem item,
CancellationToken cancellationToken)
{
- return this.InvokeFilterMethodAsync(
+ return this.InvokeProcessorMethodAsync(
context, item, ConventionBasedChangeSetConstants.FilterMethodNamePostFilterSuffix);
}
@@ -64,26 +64,21 @@ private static string GetMethodName(ChangeSetItem item, string suffix)
case ChangeSetItemType.DataModification:
DataModificationItem dataModification = (DataModificationItem)item;
string operationName = null;
- if (dataModification.ChangeSetItemAction == ChangeSetItemAction.Insert)
+ if (dataModification.DataModificationItemAction == DataModificationItemAction.Insert)
{
operationName = ConventionBasedChangeSetConstants.FilterMethodDataModificationInsert;
}
- else if (dataModification.ChangeSetItemAction == ChangeSetItemAction.Update)
+ else if (dataModification.DataModificationItemAction == DataModificationItemAction.Update)
{
operationName = ConventionBasedChangeSetConstants.FilterMethodDataModificationUpdate;
}
- else if (dataModification.ChangeSetItemAction == ChangeSetItemAction.Remove)
+ else if (dataModification.DataModificationItemAction == DataModificationItemAction.Remove)
{
operationName = ConventionBasedChangeSetConstants.FilterMethodDataModificationDelete;
}
return operationName + suffix + dataModification.EntitySetName;
- case ChangeSetItemType.ActionInvocation:
- ActionInvocationItem actionItem = (ActionInvocationItem)item;
- return ConventionBasedChangeSetConstants.FilterMethodActionInvocationExecute +
- suffix + actionItem.ActionName;
-
default:
throw new InvalidOperationException(string.Format(
CultureInfo.InvariantCulture, Resources.InvalidChangeSetEntryType, item.Type));
@@ -98,10 +93,6 @@ private static object[] GetParameters(ChangeSetItem item)
DataModificationItem dataModification = (DataModificationItem)item;
return new object[] { dataModification.Entity };
- case ChangeSetItemType.ActionInvocation:
- ActionInvocationItem actionItem = (ActionInvocationItem)item;
- return actionItem.GetArgumentArray();
-
default:
throw new InvalidOperationException(string.Format(
CultureInfo.InvariantCulture, Resources.InvalidChangeSetEntryType, item.Type));
@@ -114,13 +105,13 @@ private static bool ParametersMatch(ParameterInfo[] methodParameters, object[] p
&& !methodParameters.Where((mp, i) => !mp.ParameterType.IsInstanceOfType(parameters[i])).Any();
}
- private Task InvokeFilterMethodAsync(
+ private Task InvokeProcessorMethodAsync(
SubmitContext context,
ChangeSetItem item,
string methodNameSuffix)
{
- string methodName = ConventionBasedChangeSetItemProcessor.GetMethodName(item, methodNameSuffix);
- object[] parameters = ConventionBasedChangeSetItemProcessor.GetParameters(item);
+ string methodName = GetMethodName(item, methodNameSuffix);
+ object[] parameters = GetParameters(item);
MethodInfo method = this.targetType.GetQualifiedMethod(methodName);
@@ -140,7 +131,7 @@ private Task InvokeFilterMethodAsync(
}
ParameterInfo[] methodParameters = method.GetParameters();
- if (ConventionBasedChangeSetItemProcessor.ParametersMatch(methodParameters, parameters))
+ if (ParametersMatch(methodParameters, parameters))
{
object result = method.Invoke(target, parameters);
Task resultTask = result as Task;
diff --git a/src/Microsoft.Restier.Core/Conventions/ConventionBasedOperationProcessor.cs b/src/Microsoft.Restier.Core/Conventions/ConventionBasedOperationProcessor.cs
new file mode 100644
index 00000000..155cbce1
--- /dev/null
+++ b/src/Microsoft.Restier.Core/Conventions/ConventionBasedOperationProcessor.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Restier.Core.Operation;
+
+namespace Microsoft.Restier.Core.Conventions
+{
+ ///
+ /// A convention-based change set item filter.
+ ///
+ internal class ConventionBasedOperationProcessor : IOperationProcessor
+ {
+ private Type targetType;
+
+ private ConventionBasedOperationProcessor(Type targetType)
+ {
+ Ensure.NotNull(targetType, "targetType");
+ this.targetType = targetType;
+ }
+
+ ///
+ public static void ApplyTo(
+ IServiceCollection services,
+ Type targetType)
+ {
+ Ensure.NotNull(services, "services");
+ Ensure.NotNull(targetType, "targetType");
+ services.AddService(
+ (sp, next) => new ConventionBasedOperationProcessor(targetType));
+ }
+
+ ///
+ public Task OnExecutingOperationAsync(
+ OperationContext context,
+ CancellationToken cancellationToken)
+ {
+ return this.InvokeProcessorMethodAsync(
+ context, ConventionBasedChangeSetConstants.FilterMethodNamePreFilterSuffix);
+ }
+
+ ///
+ public Task OnExecutedOperationAsync(
+ OperationContext context,
+ CancellationToken cancellationToken)
+ {
+ return this.InvokeProcessorMethodAsync(
+ context, ConventionBasedChangeSetConstants.FilterMethodNamePostFilterSuffix);
+ }
+
+ private static bool ParametersMatch(ParameterInfo[] methodParameters, object[] parameters)
+ {
+ return methodParameters.Length == parameters.Length
+ && !methodParameters.Where((mp, i) => !mp.ParameterType.IsInstanceOfType(parameters[i])).Any();
+ }
+
+ private Task InvokeProcessorMethodAsync(
+ OperationContext context,
+ string methodNameSuffix)
+ {
+ string methodName = ConventionBasedChangeSetConstants.FilterMethodActionInvocationExecute +
+ methodNameSuffix + context.OperationName;
+ object[] parameters = null;
+ if (context.ParametersValue != null)
+ {
+ context.ParametersValue.ToArray();
+ }
+
+ MethodInfo method = this.targetType.GetQualifiedMethod(methodName);
+
+ if (method != null &&
+ (method.ReturnType == typeof(void) ||
+ typeof(Task).IsAssignableFrom(method.ReturnType)))
+ {
+ object target = null;
+ if (!method.IsStatic)
+ {
+ target = context.GetApiService();
+ if (target == null ||
+ !this.targetType.IsInstanceOfType(target))
+ {
+ return Task.WhenAll();
+ }
+ }
+
+ ParameterInfo[] methodParameters = method.GetParameters();
+ if (ParametersMatch(methodParameters, parameters))
+ {
+ object result = method.Invoke(target, parameters);
+ Task resultTask = result as Task;
+ if (resultTask != null)
+ {
+ return resultTask;
+ }
+ }
+ }
+
+ return Task.WhenAll();
+ }
+ }
+}
diff --git a/src/Microsoft.Restier.Core/Conventions/ConventionBasedEntitySetProcessor.cs b/src/Microsoft.Restier.Core/Conventions/ConventionBasedQueryExpressionProcessor.cs
similarity index 95%
rename from src/Microsoft.Restier.Core/Conventions/ConventionBasedEntitySetProcessor.cs
rename to src/Microsoft.Restier.Core/Conventions/ConventionBasedQueryExpressionProcessor.cs
index 984a5e28..5bb5ada2 100644
--- a/src/Microsoft.Restier.Core/Conventions/ConventionBasedEntitySetProcessor.cs
+++ b/src/Microsoft.Restier.Core/Conventions/ConventionBasedQueryExpressionProcessor.cs
@@ -5,22 +5,20 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
-using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OData.Edm;
-using Microsoft.OData.Edm.Library;
using Microsoft.Restier.Core.Query;
namespace Microsoft.Restier.Core.Conventions
{
///
- /// A convention-based query expression filter on entity set.
+ /// A convention-based query expression processor which will apply OnFilter logic into query expression.
///
- internal class ConventionBasedEntitySetProcessor : IQueryExpressionProcessor
+ internal class ConventionBasedQueryExpressionProcessor : IQueryExpressionProcessor
{
private Type targetType;
- private ConventionBasedEntitySetProcessor(Type targetType)
+ private ConventionBasedQueryExpressionProcessor(Type targetType)
{
this.targetType = targetType;
}
@@ -36,7 +34,7 @@ public static void ApplyTo(
Ensure.NotNull(services, "services");
Ensure.NotNull(targetType, "targetType");
services.AddService(
- (sp, next) => new ConventionBasedEntitySetProcessor(targetType)
+ (sp, next) => new ConventionBasedQueryExpressionProcessor(targetType)
{
Inner = next,
});
diff --git a/src/Microsoft.Restier.Core/DataSourceStub.cs b/src/Microsoft.Restier.Core/DataSourceStub.cs
index 3c916e0c..ecf93175 100644
--- a/src/Microsoft.Restier.Core/DataSourceStub.cs
+++ b/src/Microsoft.Restier.Core/DataSourceStub.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
-using System.Collections.Generic;
using System.Linq;
using Microsoft.Restier.Core.Properties;
@@ -90,104 +89,5 @@ public static TResult GetPropertyValue(
{
throw new InvalidOperationException(Resources.DoNotCallDataSourceStubMethodDirectly);
}
-
- ///
- /// Identifies an entity set or results of a call to a function import.
- /// TODO reserve for function/action supports
- ///
- ///
- /// The type of the elements in the results.
- ///
- ///
- /// The name of an entity set or function import.
- ///
- ///
- /// If is a function import,
- /// the arguments to be passed to the function import.
- ///
- ///
- /// A representation of the entity set or
- /// results of a call to the function import.
- ///
- internal static IEnumerable Results(
- string name, params object[] arguments)
- {
- throw new InvalidOperationException(Resources.DoNotCallDataSourceStubMethodDirectly);
- }
-
- ///
- /// Identifies a singleton or result of a
- /// call to a singular function import.
- /// TODO reserve for function/action supports
- ///
- ///
- /// The type of the result.
- ///
- ///
- /// The name of a singleton or singular function import.
- ///
- ///
- /// If is a singular function import,
- /// the arguments to be passed to the singular function import.
- ///
- ///
- /// A representation of the singleton or result
- /// of a call to the singular function import.
- ///
- internal static TResult Result(
- string name, params object[] arguments)
- {
- throw new InvalidOperationException(Resources.DoNotCallDataSourceStubMethodDirectly);
- }
-
- ///
- /// Identifies the results of a call to a function.
- /// TODO reserve for function/action supports
- ///
- ///
- /// The type of the elements in the results.
- ///
- ///
- /// The name of a namespace containing the function.
- ///
- ///
- /// The name of a function.
- ///
- ///
- /// The arguments to be passed to the function.
- ///
- ///
- /// A representation of the results of a call to the function.
- ///
- internal static IEnumerable Results(
- string namespaceName, string name, params object[] arguments)
- {
- throw new InvalidOperationException(Resources.DoNotCallDataSourceStubMethodDirectly);
- }
-
- ///
- /// Identifies the result of a call to a singular function.
- /// TODO reserve for function/action supports
- ///
- ///
- /// The type of the result.
- ///
- ///
- /// The name of a namespace containing the singular function.
- ///
- ///
- /// The name of a singular function.
- ///
- ///
- /// The arguments to be passed to the singular function.
- ///
- ///
- /// A representation of the result of a call to the singular function.
- ///
- internal static TResult Result(
- string namespaceName, string name, params object[] arguments)
- {
- throw new InvalidOperationException(Resources.DoNotCallDataSourceStubMethodDirectly);
- }
}
}
diff --git a/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj b/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj
index 2bc5d512..c700ad04 100644
--- a/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj
+++ b/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj
@@ -51,12 +51,13 @@
Shared\TypeExtensions.cs
+
-
+
@@ -72,6 +73,7 @@
+
diff --git a/src/Microsoft.Restier.Core/Operation/IOperationProcessor.cs b/src/Microsoft.Restier.Core/Operation/IOperationProcessor.cs
new file mode 100644
index 00000000..7a7030b5
--- /dev/null
+++ b/src/Microsoft.Restier.Core/Operation/IOperationProcessor.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Restier.Core.Operation
+{
+ ///
+ /// Represents a operation processor.
+ ///
+ public interface IOperationProcessor
+ {
+ ///
+ /// Asynchronously applies logic before a operation is executed.
+ ///
+ ///
+ /// The operation context.
+ ///
+ ///
+ /// A cancellation token.
+ ///
+ ///
+ /// A task that represents the asynchronous operation.
+ ///
+ Task OnExecutingOperationAsync(
+ OperationContext context,
+ CancellationToken cancellationToken);
+
+ ///
+ /// Asynchronously applies logic after an operation is executed.
+ ///
+ ///
+ /// The submit context.
+ ///
+ ///
+ /// A cancellation token.
+ ///
+ ///
+ /// A task that represents the asynchronous operation.
+ ///
+ Task OnExecutedOperationAsync(
+ OperationContext context,
+ CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Microsoft.Restier.Core/Operation/OperationContext.cs b/src/Microsoft.Restier.Core/Operation/OperationContext.cs
index f69b2722..8d067bc6 100644
--- a/src/Microsoft.Restier.Core/Operation/OperationContext.cs
+++ b/src/Microsoft.Restier.Core/Operation/OperationContext.cs
@@ -2,12 +2,14 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Restier.Core.Operation
{
///
/// Represents context under which a operation is executed.
+ /// One instance created for one execution of one operation.
///
public class OperationContext : InvocationContext
{
@@ -15,6 +17,7 @@ public class OperationContext : InvocationContext
private readonly Func getParameterValueFunc;
private readonly bool isFunction;
private readonly IQueryable bindingParameterValue;
+ private ICollection