diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 46a66f80..52915e84 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -9,6 +9,8 @@ [assembly: SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Scope = "type", Target = "Microsoft.Restier.Core.Submit.ChangeSetValidationException", Justification = "We do not intend to support serialization of this exception yet")] [assembly: SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods", Scope = "member", Target = "Microsoft.Restier.Publishers.OData.Batch.RestierBatchChangeSetRequestItem.#DisposeResponses(System.Collections.Generic.IEnumerable`1)")] [assembly: SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Scope = "member", Target = "Microsoft.Restier.Core.ApiBase.#Dispose()", Justification = "Need to do some clean up before the virtual Dispose(disposing) method gets called.")] +[assembly: SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Scope = "type", Target = "Microsoft.Restier.Core.ApiContext")] +[assembly: SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Scope = "member", Target = "Microsoft.Restier.Core.ApiContext.#Dispose()")] [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "These assemblies are delay-signed.")] [assembly: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "member", Target = "Microsoft.Restier.Providers.EntityFramework.ModelProducer.#ProduceModelAsync(Microsoft.Restier.Core.Model.ModelContext,System.Threading.CancellationToken)")] [assembly: SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Scope = "type", Target = "Microsoft.Restier.Core.Submit.ChangeSetValidationResults")] @@ -191,7 +193,7 @@ #endregion #region CA1812 Uninstantiated internal classes -[assembly: SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope = "type", Target = "Microsoft.Restier.Core.ApiBase+ApiHolder")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope = "type", Target = "Microsoft.Restier.Core.ApiConfiguration")] [assembly: SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope = "type", Target = "Microsoft.Restier.Core.ConventionBasedChangeSetItemValidator")] [assembly: SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope = "type", Target = "Microsoft.Restier.Core.PropertyBag")] [assembly: SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope = "type", Target = "Microsoft.Restier.Providers.EntityFramework.ModelProducer")] diff --git a/src/Microsoft.Restier.Core/ApiBase.cs b/src/Microsoft.Restier.Core/ApiBase.cs index a2b74469..7e73fb10 100644 --- a/src/Microsoft.Restier.Core/ApiBase.cs +++ b/src/Microsoft.Restier.Core/ApiBase.cs @@ -21,7 +21,11 @@ namespace Microsoft.Restier.Core /// public abstract class ApiBase : IDisposable { - private ApiConfiguration apiConfiguration; + private static ConcurrentDictionary> publisherServicesCallback = + new ConcurrentDictionary>(); + + private static Action emptyConfig = _ => { }; + private ApiContext apiContext; private IServiceProvider serviceProvider; @@ -73,22 +77,6 @@ public ApiContext Context /// public bool IsDisposed { get; private set; } - /// - /// Gets the API configuration for this API. - /// - public ApiConfiguration Configuration - { - get - { - if (this.apiConfiguration == null) - { - this.apiConfiguration = serviceProvider.GetService(); - } - - return this.apiConfiguration; - } - } - /// /// Configure services for this API. /// @@ -107,11 +95,49 @@ public static IServiceCollection ConfigureApi(Type apiType, IServiceCollection s .AddConventionBasedServices(apiType); // This is used to add the publisher's services - ApiConfiguration.GetPublisherServiceCallback(apiType)(services); + GetPublisherServiceCallback(apiType)(services); return services; } + /// + /// Adds a configuration procedure for apiType. + /// This is expected to be called by publisher like WebApi to add services. + /// + /// + /// The Api Type. + /// + /// + /// An action that will be called during the configuration of apiType. + /// + [CLSCompliant(false)] + public static void AddPublisherServices(Type apiType, Action configurationCallback) + { + publisherServicesCallback.AddOrUpdate( + apiType, + configurationCallback, + (type, existing) => existing + configurationCallback); + } + + /// + /// Get publisher registering service callback for specified Api. + /// + /// + /// The Api type of which to get the publisher registering service callback. + /// + /// The service registering callback. + [CLSCompliant(false)] + public static Action GetPublisherServiceCallback(Type apiType) + { + Action val; + if (publisherServicesCallback.TryGetValue(apiType, out val)) + { + return val; + } + + return emptyConfig; + } + /// /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. diff --git a/src/Microsoft.Restier.Core/ApiBaseExtensions.cs b/src/Microsoft.Restier.Core/ApiBaseExtensions.cs deleted file mode 100644 index 2317941e..00000000 --- a/src/Microsoft.Restier.Core/ApiBaseExtensions.cs +++ /dev/null @@ -1,272 +0,0 @@ -// 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.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.OData.Edm; -using Microsoft.Restier.Core.Query; -using Microsoft.Restier.Core.Submit; - -namespace Microsoft.Restier.Core -{ - /// - /// Represents the API engine and provides a set of static - /// (Shared in Visual Basic) methods for interacting with objects - /// that implement . - /// - public static class ApiBaseExtensions // TODO GitHubIssue#25,#26 : transactions, exception filters - { - #region Model - - /// - /// Asynchronously gets an API model for an API. - /// - /// - /// An API. - /// - /// - /// An optional cancellation token. - /// - /// - /// A task that represents the asynchronous - /// operation whose result is the API model. - /// - public static Task GetModelAsync( - this ApiBase api, - CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.NotNull(api, "api"); - - return api.Context.GetModelAsync(cancellationToken); - } - - #endregion - - #region GetQueryableSource - - /// - /// Gets a queryable source of data exposed by an API. - /// - /// - /// An API. - /// - /// - /// The name of an entity set, singleton or composable function import. - /// - /// - /// If is a composable function import, - /// the arguments to be passed to the composable function import. - /// - /// - /// A queryable source. - /// - /// - /// - /// If the name identifies a singleton or a composable function import - /// whose result is a singleton, the resulting queryable source will - /// be configured such that it represents exactly zero or one result. - /// - /// - /// Note that the resulting queryable source cannot be synchronously - /// enumerated as the API engine only operates asynchronously. - /// - /// - public static IQueryable GetQueryableSource( - this ApiBase api, - string name, - params object[] arguments) - { - Ensure.NotNull(api, "api"); - - return api.Context.GetQueryableSource(name, arguments); - } - - /// - /// Gets a queryable source of data exposed by an API. - /// - /// - /// An API. - /// - /// - /// The name of a namespace containing a composable function. - /// - /// - /// The name of a composable function. - /// - /// - /// The arguments to be passed to the composable function. - /// - /// - /// A queryable source. - /// - /// - /// - /// If the name identifies a composable function whose result is a - /// singleton, the resulting queryable source will be configured such - /// that it represents exactly zero or one result. - /// - /// - /// Note that the resulting queryable source cannot be synchronously - /// enumerated, as the API engine only operates asynchronously. - /// - /// - public static IQueryable GetQueryableSource( - this ApiBase api, - string namespaceName, - string name, - params object[] arguments) - { - Ensure.NotNull(api, "api"); - - return api.Context.GetQueryableSource(namespaceName, name, arguments); - } - - /// - /// Gets a queryable source of data exposed by an API. - /// - /// - /// The type of the elements in the queryable source. - /// - /// - /// An API. - /// - /// - /// The name of an entity set, singleton or composable function import. - /// - /// - /// If is a composable function import, - /// the arguments to be passed to the composable function import. - /// - /// - /// A queryable source. - /// - /// - /// - /// If the name identifies a singleton or a composable function import - /// whose result is a singleton, the resulting queryable source will - /// be configured such that it represents exactly zero or one result. - /// - /// - /// Note that the resulting queryable source cannot be synchronously - /// enumerated, as the API engine only operates asynchronously. - /// - /// - public static IQueryable GetQueryableSource( - this ApiBase api, - string name, - params object[] arguments) - { - Ensure.NotNull(api, "api"); - - return api.Context.GetQueryableSource(name, arguments); - } - - /// - /// Gets a queryable source of data exposed by an API. - /// - /// - /// The type of the elements in the queryable source. - /// - /// - /// An API. - /// - /// - /// The name of a namespace containing a composable function. - /// - /// - /// The name of a composable function. - /// - /// - /// The arguments to be passed to the composable function. - /// - /// - /// A queryable source. - /// - /// - /// - /// If the name identifies a composable function whose result is a - /// singleton, the resulting queryable source will be configured such - /// that it represents exactly zero or one result. - /// - /// - /// Note that the resulting queryable source cannot be synchronously - /// enumerated, as the API engine only operates asynchronously. - /// - /// - public static IQueryable GetQueryableSource( - this ApiBase api, - string namespaceName, - string name, - params object[] arguments) - { - Ensure.NotNull(api, "api"); - - return api.Context.GetQueryableSource(namespaceName, name, arguments); - } - - #endregion - - #region Query - - /// - /// Asynchronously queries for data exposed by an API. - /// - /// - /// An API. - /// - /// - /// A query request. - /// - /// - /// An optional cancellation token. - /// - /// - /// A task that represents the asynchronous - /// operation whose result is a query result. - /// - public static Task QueryAsync( - this ApiBase api, - QueryRequest request, - CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.NotNull(api, "api"); - - return api.Context.QueryAsync(request, cancellationToken); - } - - #endregion - - #region Submit - - /// - /// Asynchronously submits changes made to an API. - /// - /// - /// 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 Task SubmitAsync( - this ApiBase api, - ChangeSet changeSet = null, - CancellationToken cancellationToken = default(CancellationToken)) - { - Ensure.NotNull(api, "api"); - - return api.Context.SubmitAsync(changeSet, cancellationToken); - } - - #endregion - } -} diff --git a/src/Microsoft.Restier.Core/ApiConfiguration.cs b/src/Microsoft.Restier.Core/ApiConfiguration.cs index 6cd200d2..6c56b838 100644 --- a/src/Microsoft.Restier.Core/ApiConfiguration.cs +++ b/src/Microsoft.Restier.Core/ApiConfiguration.cs @@ -1,11 +1,8 @@ // 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.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; namespace Microsoft.Restier.Core @@ -23,55 +20,12 @@ namespace Microsoft.Restier.Core /// or scoped, in which case there will be one instances of the services for each scope. /// /// - public class ApiConfiguration + internal class ApiConfiguration { - private static ConcurrentDictionary> publisherServicesCallback = - new ConcurrentDictionary>(); - - private static Action emptyConfig = _ => { }; - private Task modelTask; internal IEdmModel Model { get; private set; } - /// - /// Adds a configuration procedure for apiType. - /// This is expected to be called by publisher like WebApi to add services. - /// - /// - /// The Api Type. - /// - /// - /// An action that will be called during the configuration of apiType. - /// - [CLSCompliant(false)] - public static void AddPublisherServices(Type apiType, Action configurationCallback) - { - publisherServicesCallback.AddOrUpdate( - apiType, - configurationCallback, - (type, existing) => existing + configurationCallback); - } - - /// - /// Get publisher registering service callback for specified Api. - /// - /// - /// The Api type of which to get the publisher registering service callback. - /// - /// The service registering callback. - [CLSCompliant(false)] - public static Action GetPublisherServiceCallback(Type apiType) - { - Action val; - if (publisherServicesCallback.TryGetValue(apiType, out val)) - { - return val; - } - - return emptyConfig; - } - internal TaskCompletionSource CompeteModelGeneration(out Task running) { var source = new TaskCompletionSource(TaskCreationOptions.AttachedToParent); diff --git a/src/Microsoft.Restier.Core/ApiContext.cs b/src/Microsoft.Restier.Core/ApiContext.cs index 925e281b..084a4be6 100644 --- a/src/Microsoft.Restier.Core/ApiContext.cs +++ b/src/Microsoft.Restier.Core/ApiContext.cs @@ -12,9 +12,10 @@ namespace Microsoft.Restier.Core /// /// An API context is an instantiation of an API configuration. /// - public class ApiContext + public class ApiContext : IDisposable { private IServiceProvider serviceProvider; + private ApiConfiguration apiConfiguration; /// /// Initializes a new instance of the class. @@ -22,21 +23,31 @@ public class ApiContext /// /// The service provider. /// - /// - /// An API configuration. - /// - public ApiContext(IServiceProvider provider, ApiConfiguration configuration) + public ApiContext(IServiceProvider provider) { - Ensure.NotNull(configuration, "configuration"); - - this.Configuration = configuration; this.serviceProvider = provider; } /// - /// Gets the API configuration. + /// Gets a value indicating whether this API context has been disposed. + /// + public bool IsDisposed { get; private set; } + + /// + /// Gets the API configuration for this API. /// - public ApiConfiguration Configuration { get; private set; } + internal ApiConfiguration Configuration + { + get + { + if (this.apiConfiguration == null) + { + this.apiConfiguration = serviceProvider.GetService(); + } + + return this.apiConfiguration; + } + } /// /// Gets the which contains all services of this . @@ -45,5 +56,21 @@ internal IServiceProvider ServiceProvider { get { return this.serviceProvider; } } + + /// + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (this.IsDisposed) + { + return; + } + + this.IsDisposed = true; + + GC.SuppressFinalize(this); + } } } diff --git a/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj b/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj index b8b53ed8..2cd063bc 100644 --- a/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj +++ b/src/Microsoft.Restier.Core/Microsoft.Restier.Core.csproj @@ -70,7 +70,6 @@ - diff --git a/src/Microsoft.Restier.Core/Operation/IOperationExecutor.cs b/src/Microsoft.Restier.Core/Operation/IOperationExecutor.cs index 54a478c0..f22a3353 100644 --- a/src/Microsoft.Restier.Core/Operation/IOperationExecutor.cs +++ b/src/Microsoft.Restier.Core/Operation/IOperationExecutor.cs @@ -16,9 +16,6 @@ public interface IOperationExecutor /// /// Asynchronously executes an operation. /// - /// - /// A class instance with have operation implemented and will be used for reflection call. - /// /// /// The operation context. /// @@ -29,7 +26,6 @@ public interface IOperationExecutor /// A task that represents the asynchronous /// operation whose result is a operation result. /// - Task ExecuteOperationAsync( - object instanceImplementMethod, OperationContext context, CancellationToken cancellationToken); + Task ExecuteOperationAsync(OperationContext context, CancellationToken cancellationToken); } } diff --git a/src/Microsoft.Restier.Providers.EntityFramework/EntityFrameworkApi.cs b/src/Microsoft.Restier.Providers.EntityFramework/EntityFrameworkApi.cs index f57d3b8f..ed5117e5 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/EntityFrameworkApi.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/EntityFrameworkApi.cs @@ -75,7 +75,7 @@ protected T DbContext services.AddEfProviderServices(); // This is used to add the publisher's services - ApiConfiguration.GetPublisherServiceCallback(apiType)(services); + GetPublisherServiceCallback(apiType)(services); return services; } diff --git a/src/Microsoft.Restier.Publishers.OData/Batch/RestierBatchChangeSetRequestItem.cs b/src/Microsoft.Restier.Publishers.OData/Batch/RestierBatchChangeSetRequestItem.cs index 8095c1c7..7d790cdf 100644 --- a/src/Microsoft.Restier.Publishers.OData/Batch/RestierBatchChangeSetRequestItem.cs +++ b/src/Microsoft.Restier.Publishers.OData/Batch/RestierBatchChangeSetRequestItem.cs @@ -88,7 +88,7 @@ public override async Task SendRequestAsync( internal async Task SubmitChangeSet(HttpRequestMessage request, ChangeSet changeSet) { var requestContainer = request.GetRequestContainer(); - using (var api = requestContainer.GetService()) + using (var api = requestContainer.GetService()) { SubmitResult submitResults = await api.SubmitAsync(changeSet); } diff --git a/src/Microsoft.Restier.Publishers.OData/Extensions.cs b/src/Microsoft.Restier.Publishers.OData/Extensions.cs index c1b3a8c4..37fd3fc5 100644 --- a/src/Microsoft.Restier.Publishers.OData/Extensions.cs +++ b/src/Microsoft.Restier.Publishers.OData/Extensions.cs @@ -69,9 +69,9 @@ public static bool IsConcurrencyCheckEnabled(this IEdmModel model, IEdmEntitySet } public static IReadOnlyDictionary CreatePropertyDictionary( - this Delta entity, IEdmStructuredType edmType, ApiBase api, bool isCreation) + this Delta entity, IEdmStructuredType edmType, ApiContext apiContext, bool isCreation) { - var propertiesAttributes = RetrievePropertiesAttributes(edmType, api); + var propertiesAttributes = RetrievePropertiesAttributes(edmType, apiContext); Dictionary propertyValues = new Dictionary(); foreach (string propertyName in entity.GetChangedPropertyNames()) @@ -93,7 +93,7 @@ public static IReadOnlyDictionary CreatePropertyDictionary( var complexObj = value as EdmComplexObject; if (complexObj != null) { - value = CreatePropertyDictionary(complexObj, complexObj.ActualEdmType, api, isCreation); + value = CreatePropertyDictionary(complexObj, complexObj.ActualEdmType, apiContext, isCreation); } propertyValues.Add(propertyName, value); @@ -104,7 +104,7 @@ public static IReadOnlyDictionary CreatePropertyDictionary( } public static IDictionary RetrievePropertiesAttributes( - IEdmStructuredType edmType, ApiBase api) + IEdmStructuredType edmType, ApiContext apiContext) { IDictionary propertiesAttributes; if (typePropertiesAttributes.TryGetValue(edmType, out propertiesAttributes)) @@ -112,7 +112,7 @@ public static IDictionary RetrievePropertiesAttribut return propertiesAttributes; } - var model = api.Context.GetModelAsync().Result; + var model = apiContext.GetModelAsync().Result; foreach (var property in edmType.DeclaredProperties) { var annotations = model.FindVocabularyAnnotations(property); @@ -164,7 +164,7 @@ public static IDictionary RetrievePropertiesAttribut return propertiesAttributes; } - public static Type GetClrType(this IEdmType edmType, ApiBase api) + public static Type GetClrType(this IEdmType edmType, ApiContext api) { IEdmModel edmModel = api.GetModelAsync().Result; diff --git a/src/Microsoft.Restier.Publishers.OData/HttpRequestMessageExtensions.cs b/src/Microsoft.Restier.Publishers.OData/HttpRequestMessageExtensions.cs index 70361762..f6606d81 100644 --- a/src/Microsoft.Restier.Publishers.OData/HttpRequestMessageExtensions.cs +++ b/src/Microsoft.Restier.Publishers.OData/HttpRequestMessageExtensions.cs @@ -4,7 +4,6 @@ using System; using System.ComponentModel; using System.Net.Http; -using Microsoft.Restier.Core; using Microsoft.Restier.Publishers.OData.Batch; namespace Microsoft.Restier.Publishers.OData @@ -16,7 +15,6 @@ namespace Microsoft.Restier.Publishers.OData internal static class HttpRequestMessageExtensions { private const string ChangeSetKey = "Microsoft.Restier.Submit.ChangeSet"; - private const string ApiInstanceKey = "Microsoft.Restier.Core.ApiInstance"; /// /// Sets the to the . @@ -46,36 +44,5 @@ public static RestierChangeSetProperty GetChangeSet(this HttpRequestMessage requ return null; } - - /// - /// Gets the API instance from the . - /// - /// The HTTP request. - /// The API instance. - internal static ApiBase GetApiInstance(this HttpRequestMessage request) - { - Ensure.NotNull(request, "request"); - - object value; - if (request.Properties.TryGetValue(ApiInstanceKey, out value)) - { - return value as ApiBase; - } - - return null; - } - - /// - /// Sets the API instance to the . - /// - /// The HTTP request. - /// The API instance. - internal static void SetApiInstance(this HttpRequestMessage request, ApiBase apiInstance) - { - Ensure.NotNull(request, "request"); - Ensure.NotNull(apiInstance, "apiInstance"); - - request.Properties[ApiInstanceKey] = apiInstance; - } } } diff --git a/src/Microsoft.Restier.Publishers.OData/Operation/OperationExecutor.cs b/src/Microsoft.Restier.Publishers.OData/Operation/OperationExecutor.cs index a16feac2..0be1a08a 100644 --- a/src/Microsoft.Restier.Publishers.OData/Operation/OperationExecutor.cs +++ b/src/Microsoft.Restier.Publishers.OData/Operation/OperationExecutor.cs @@ -22,14 +22,14 @@ namespace Microsoft.Restier.Publishers.OData.Operation internal class OperationExecutor : IOperationExecutor { public async Task ExecuteOperationAsync( - object instanceImplementMethod, OperationContext context, CancellationToken cancellationToken) + OperationContext context, CancellationToken cancellationToken) { // Authorization check await InvokeAuthorizers(context, cancellationToken); // model build does not support operation with same name // So method with same name but different signature is not considered. - MethodInfo method = instanceImplementMethod.GetType().GetMethod( + MethodInfo method = context.ImplementInstance.GetType().GetMethod( context.OperationName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); if (method == null) @@ -86,7 +86,7 @@ public async Task ExecuteOperationAsync( // Invoke preprocessing on the operation execution PerformPreEvent(context, cancellationToken); - var result = InvokeOperation(instanceImplementMethod, method, parameters, model); + var result = InvokeOperation(context.ImplementInstance, method, parameters, model); // Invoke preprocessing on the operation execution PerformPostEvent(context, cancellationToken); diff --git a/src/Microsoft.Restier.Publishers.OData/Query/RestierQueryBuilder.cs b/src/Microsoft.Restier.Publishers.OData/Query/RestierQueryBuilder.cs index 4d2f80a5..47235f79 100644 --- a/src/Microsoft.Restier.Publishers.OData/Query/RestierQueryBuilder.cs +++ b/src/Microsoft.Restier.Publishers.OData/Query/RestierQueryBuilder.cs @@ -17,7 +17,7 @@ internal class RestierQueryBuilder { private const string DefaultNameOfParameterExpression = "currentValue"; - private readonly ApiBase api; + private readonly ApiContext apiContext; private readonly ODataPath path; private readonly IDictionary> handlers = new Dictionary>(); @@ -25,11 +25,11 @@ internal class RestierQueryBuilder private IQueryable queryable; private Type currentType; - public RestierQueryBuilder(ApiBase api, ODataPath path) + public RestierQueryBuilder(ApiContext apiContext, ODataPath path) { - Ensure.NotNull(api, "api"); + Ensure.NotNull(apiContext, "apiContext"); Ensure.NotNull(path, "path"); - this.api = api; + this.apiContext = apiContext; this.path = path; this.handlers[typeof(EntitySetSegment)] = this.HandleEntitySetPathSegment; @@ -139,7 +139,7 @@ private void HandleEntitySetPathSegment(ODataPathSegment segment) { var entitySetPathSegment = (EntitySetSegment)segment; var entitySet = entitySetPathSegment.EntitySet; - this.queryable = this.api.GetQueryableSource(entitySet.Name, (object[])null); + this.queryable = this.apiContext.GetQueryableSource(entitySet.Name, (object[])null); this.currentType = this.queryable.ElementType; } @@ -147,7 +147,7 @@ private void HandleSingletonPathSegment(ODataPathSegment segment) { var singletonPathSegment = (SingletonSegment)segment; var singleton = singletonPathSegment.Singleton; - this.queryable = this.api.GetQueryableSource(singleton.Name, (object[])null); + this.queryable = this.apiContext.GetQueryableSource(singleton.Name, (object[])null); this.currentType = this.queryable.ElementType; } @@ -274,7 +274,7 @@ private void HandleEntityTypeSegment(ODataPathSegment segment) if (edmType.TypeKind == EdmTypeKind.Entity) { - this.currentType = edmType.GetClrType(api); + this.currentType = edmType.GetClrType(apiContext); this.queryable = ExpressionHelpers.OfType(this.queryable, this.currentType); } } diff --git a/src/Microsoft.Restier.Publishers.OData/RestierController.cs b/src/Microsoft.Restier.Publishers.OData/RestierController.cs index 1e450fe3..4dab69af 100644 --- a/src/Microsoft.Restier.Publishers.OData/RestierController.cs +++ b/src/Microsoft.Restier.Publishers.OData/RestierController.cs @@ -18,6 +18,7 @@ using System.Web.OData.Query; using System.Web.OData.Results; using System.Web.OData.Routing; +using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; using Microsoft.Restier.Core; @@ -43,23 +44,24 @@ public class RestierController : ODataController private const string IfMatchKey = "@IfMatchKey"; private const string IfNoneMatchKey = "@IfNoneMatchKey"; - private ApiBase api; + private ApiContext apiContext; private bool shouldReturnCount; private bool shouldWriteRawValue; /// /// Gets the API associated with this controller. /// - private ApiBase Api + private ApiContext ApiContext { get { - if (this.api == null) + if (this.apiContext == null) { - this.api = this.Request.GetApiInstance(); + var provider = Request.GetRequestContainer(); + this.apiContext = provider.GetService(); } - return this.api; + return this.apiContext; } } @@ -160,12 +162,12 @@ public async Task Post(EdmEntityObject edmEntityObject, Cance DataModificationItem postItem = new DataModificationItem( entitySet.Name, - expectedEntityType.GetClrType(Api), - actualEntityType.GetClrType(Api), + expectedEntityType.GetClrType(ApiContext), + actualEntityType.GetClrType(ApiContext), DataModificationItemAction.Insert, null, null, - edmEntityObject.CreatePropertyDictionary(actualEntityType, api, true)); + edmEntityObject.CreatePropertyDictionary(actualEntityType, apiContext, true)); RestierChangeSetProperty changeSetProperty = this.Request.GetChangeSet(); if (changeSetProperty == null) @@ -173,7 +175,7 @@ public async Task Post(EdmEntityObject edmEntityObject, Cance ChangeSet changeSet = new ChangeSet(); changeSet.Entries.Add(postItem); - SubmitResult result = await Api.SubmitAsync(changeSet, cancellationToken); + SubmitResult result = await ApiContext.SubmitAsync(changeSet, cancellationToken); } else { @@ -239,7 +241,7 @@ public async Task Delete(CancellationToken cancellationToken) DataModificationItem deleteItem = new DataModificationItem( entitySet.Name, - path.EdmType.GetClrType(Api), + path.EdmType.GetClrType(ApiContext), null, DataModificationItemAction.Remove, RestierQueryBuilder.GetPathKeyValues(path), @@ -252,7 +254,7 @@ public async Task Delete(CancellationToken cancellationToken) ChangeSet changeSet = new ChangeSet(); changeSet.Entries.Add(deleteItem); - SubmitResult result = await Api.SubmitAsync(changeSet, cancellationToken); + SubmitResult result = await ApiContext.SubmitAsync(changeSet, cancellationToken); } else { @@ -331,23 +333,6 @@ public async Task PostAction( return this.CreateQueryResponse(result, path.EdmType, false, null); } - /// - /// Disposes the API and the controller. - /// - /// Indicates whether disposing is happening. - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (this.api != null) - { - this.api.Dispose(); - } - } - - base.Dispose(disposing); - } - private static IEdmTypeReference GetTypeReference(IEdmType edmType) { Ensure.NotNull(edmType, "edmType"); @@ -406,12 +391,12 @@ private async Task Update( DataModificationItem updateItem = new DataModificationItem( entitySet.Name, - expectedEntityType.GetClrType(Api), - actualEntityType.GetClrType(Api), + expectedEntityType.GetClrType(ApiContext), + actualEntityType.GetClrType(ApiContext), DataModificationItemAction.Update, RestierQueryBuilder.GetPathKeyValues(path), propertiesInEtag, - edmEntityObject.CreatePropertyDictionary(actualEntityType, api, false)); + edmEntityObject.CreatePropertyDictionary(actualEntityType, apiContext, false)); updateItem.IsFullReplaceUpdateRequest = isFullReplaceUpdate; RestierChangeSetProperty changeSetProperty = this.Request.GetChangeSet(); @@ -420,7 +405,7 @@ private async Task Update( ChangeSet changeSet = new ChangeSet(); changeSet.Entries.Add(updateItem); - SubmitResult result = await Api.SubmitAsync(changeSet, cancellationToken); + SubmitResult result = await ApiContext.SubmitAsync(changeSet, cancellationToken); } else { @@ -443,13 +428,13 @@ private HttpResponseMessage CreateQueryResponse( { if (this.shouldReturnCount || this.shouldWriteRawValue) { - var rawResult = new RawResult(query, typeReference, this.Api.Context); + var rawResult = new RawResult(query, typeReference, this.ApiContext); singleResult = rawResult; response = this.Request.CreateResponse(HttpStatusCode.OK, rawResult); } else { - var primitiveResult = new PrimitiveResult(query, typeReference, this.Api.Context); + var primitiveResult = new PrimitiveResult(query, typeReference, this.ApiContext); singleResult = primitiveResult; response = this.Request.CreateResponse(HttpStatusCode.OK, primitiveResult); } @@ -457,7 +442,7 @@ private HttpResponseMessage CreateQueryResponse( if (typeReference.IsComplex()) { - var complexResult = new ComplexResult(query, typeReference, this.Api.Context); + var complexResult = new ComplexResult(query, typeReference, this.ApiContext); singleResult = complexResult; response = this.Request.CreateResponse(HttpStatusCode.OK, complexResult); } @@ -466,13 +451,13 @@ private HttpResponseMessage CreateQueryResponse( { if (this.shouldWriteRawValue) { - var rawResult = new RawResult(query, typeReference, this.Api.Context); + var rawResult = new RawResult(query, typeReference, this.ApiContext); singleResult = rawResult; response = this.Request.CreateResponse(HttpStatusCode.OK, rawResult); } else { - var enumResult = new EnumResult(query, typeReference, this.Api.Context); + var enumResult = new EnumResult(query, typeReference, this.ApiContext); singleResult = enumResult; response = this.Request.CreateResponse(HttpStatusCode.OK, enumResult); } @@ -496,11 +481,11 @@ private HttpResponseMessage CreateQueryResponse( if (elementType.IsPrimitive() || elementType.IsEnum()) { return this.Request.CreateResponse( - HttpStatusCode.OK, new NonResourceCollectionResult(query, typeReference, this.Api.Context)); + HttpStatusCode.OK, new NonResourceCollectionResult(query, typeReference, this.ApiContext)); } return this.Request.CreateResponse( - HttpStatusCode.OK, new ResourceSetResult(query, typeReference, this.Api.Context)); + HttpStatusCode.OK, new ResourceSetResult(query, typeReference, this.ApiContext)); } var entityResult = query.SingleOrDefault(); @@ -543,7 +528,7 @@ private HttpResponseMessage CreateQueryResponse( private IQueryable GetQuery(ODataPath path) { - RestierQueryBuilder builder = new RestierQueryBuilder(this.Api, path); + RestierQueryBuilder builder = new RestierQueryBuilder(this.ApiContext, path); IQueryable queryable = builder.BuildQuery(); this.shouldReturnCount = builder.IsCountPathSegmentPresent; this.shouldWriteRawValue = builder.IsValuePathSegmentPresent; @@ -565,7 +550,7 @@ private IQueryable ApplyQueryOptions( } HttpRequestMessageProperties properties = this.Request.ODataProperties(); - var model = Api.GetModelAsync().Result; + var model = ApiContext.GetModelAsync().Result; ODataQueryContext queryContext = new ODataQueryContext(model, queryable.ElementType, path); ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, this.Request); @@ -582,7 +567,7 @@ private IQueryable ApplyQueryOptions( } // TODO GitHubIssue#41 : Ensure stable ordering for query - ODataQuerySettings settings = Api.Context.GetApiService(); + ODataQuerySettings settings = ApiContext.GetApiService(); if (this.shouldReturnCount) { @@ -595,13 +580,13 @@ private IQueryable ApplyQueryOptions( if (queryOptions.Count != null && !applyCount) { RestierQueryExecutorOptions queryExecutorOptions = - Api.Context.GetApiService(); + ApiContext.GetApiService(); queryExecutorOptions.IncludeTotalCount = queryOptions.Count.Value; queryExecutorOptions.SetTotalCount = value => properties.TotalCount = value; } // Validate query before apply, and query setting like MaxExpansionDepth can be customized here - ODataValidationSettings validationSettings = Api.Context.GetApiService(); + ODataValidationSettings validationSettings = ApiContext.GetApiService(); queryOptions.Validate(validationSettings); // Entity count can NOT be evaluated at this point of time because the source @@ -625,7 +610,7 @@ private async Task ExecuteQuery(IQueryable queryable, CancellationTo ShouldReturnCount = this.shouldReturnCount }; - QueryResult queryResult = await Api.QueryAsync(queryRequest, cancellationToken); + QueryResult queryResult = await ApiContext.QueryAsync(queryRequest, cancellationToken); var result = queryResult.Results.AsQueryable(); return result; } @@ -654,12 +639,14 @@ private Task ExecuteOperationAsync( IQueryable bindingParameterValue, CancellationToken cancellationToken) { - var executor = Api.Context.GetApiService(); + var executor = ApiContext.GetApiService(); + var implementInstance = ApiContext.GetApiService(); + var context = new OperationContext( - getParaValueFunc, operationName, Api, isFunction, bindingParameterValue); + getParaValueFunc, operationName, implementInstance, isFunction, bindingParameterValue); context.ServiceProvider = Request.GetRequestContainer(); context.Request = Request; - var result = executor.ExecuteOperationAsync(Api, context, cancellationToken); + var result = executor.ExecuteOperationAsync(context, cancellationToken); return result; } @@ -688,7 +675,7 @@ private async Task> GetOriginalValues(IEdmEn } // return 428(Precondition Required) if entity requires concurrency check. - var model = await this.Api.Context.GetModelAsync(); + var model = await this.ApiContext.GetModelAsync(); bool needEtag = model.IsConcurrencyCheckEnabled(entitySet); if (needEtag) { diff --git a/src/Microsoft.Restier.Publishers.OData/Routing/HttpConfigurationExtensions.cs b/src/Microsoft.Restier.Publishers.OData/Routing/HttpConfigurationExtensions.cs index e4a0206f..bb315bb0 100644 --- a/src/Microsoft.Restier.Publishers.OData/Routing/HttpConfigurationExtensions.cs +++ b/src/Microsoft.Restier.Publishers.OData/Routing/HttpConfigurationExtensions.cs @@ -49,7 +49,7 @@ public static Task MapRestierRoute( { // This will be added a service to callback stored in ApiConfiguration // Callback is called by ApiBase.AddApiServices method to add real services. - ApiConfiguration.AddPublisherServices( + ApiBase.AddPublisherServices( typeof(TApi), services => { diff --git a/src/Microsoft.Restier.Publishers.OData/Routing/RestierRoutingConvention.cs b/src/Microsoft.Restier.Publishers.OData/Routing/RestierRoutingConvention.cs index 876343c9..cea4f355 100644 --- a/src/Microsoft.Restier.Publishers.OData/Routing/RestierRoutingConvention.cs +++ b/src/Microsoft.Restier.Publishers.OData/Routing/RestierRoutingConvention.cs @@ -57,10 +57,6 @@ public string SelectController(ODataPath odataPath, HttpRequestMessage request) return null; } - // Create ApiBase instance - var provider = request.GetRequestContainer(); - var apiBase = provider.GetService(); - request.SetApiInstance(apiBase); return RestierControllerName; } diff --git a/test/Microsoft.Restier.Core.Tests/Api.Tests.cs b/test/Microsoft.Restier.Core.Tests/Api.Tests.cs index 2624423f..23f8d35d 100644 --- a/test/Microsoft.Restier.Core.Tests/Api.Tests.cs +++ b/test/Microsoft.Restier.Core.Tests/Api.Tests.cs @@ -115,7 +115,7 @@ public void ApiSourceOfEntityContainerElementIsCorrect() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); var arguments = new object[0]; @@ -177,7 +177,7 @@ public void ApiSourceOfComposableFunctionIsCorrect() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); var arguments = new object[0]; @@ -244,7 +244,7 @@ public void GenericApiSourceOfEntityContainerElementIsCorrect() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); var arguments = new object[0]; @@ -311,7 +311,7 @@ public void GenericApiSourceOfComposableFunctionIsCorrect() var arguments = new object[0]; - var source = api.GetQueryableSource( + var source = api.Context.GetQueryableSource( "Namespace", "Function", arguments); Assert.Equal(typeof(DateTime), source.ElementType); Assert.True(source.Expression is MethodCallExpression); @@ -423,10 +423,10 @@ public async Task ApiQueryAsyncWithQueryReturnsResults() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); var request = new QueryRequest(api.GetQueryableSource("Test")); - var result = await api.Context.QueryAsync(request); + var result = await api.QueryAsync(request); var results = result.Results.Cast(); Assert.True(results.SequenceEqual(new[] {"Test"})); @@ -437,7 +437,7 @@ public async Task ApiQueryAsyncCorrectlyForwardsCall() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); var queryRequest = new QueryRequest( api.GetQueryableSource("Test")); @@ -451,7 +451,7 @@ public async Task ApiSubmitAsyncCorrectlyForwardsCall() { var container = new RestierContainerBuilder(typeof(TestApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); var submitResult = await api.SubmitAsync(); Assert.NotNull(submitResult.CompletedChangeSet); diff --git a/test/Microsoft.Restier.Core.Tests/ApiConfiguration.Tests.cs b/test/Microsoft.Restier.Core.Tests/ApiConfiguration.Tests.cs index cf4372e8..948274f3 100644 --- a/test/Microsoft.Restier.Core.Tests/ApiConfiguration.Tests.cs +++ b/test/Microsoft.Restier.Core.Tests/ApiConfiguration.Tests.cs @@ -15,20 +15,6 @@ namespace Microsoft.Restier.Core.Tests { public class ApiConfigurationTests { - [Fact] - public void CachedConfigurationIsCachedCorrectly() - { - var container = new RestierContainerBuilder(typeof(TestApiA)); - var provider = container.BuildContainer(); - var api = provider.GetService(); - - var configuration = api.Context.Configuration; - - ApiBase anotherApi = provider.GetService(); - var cached = anotherApi.Context.Configuration; - Assert.Same(configuration, cached); - } - [Fact] public void ConfigurationRegistersApiServicesCorrectly() { @@ -108,17 +94,11 @@ public static ServiceB serviceB public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => serviceA); services.AddService((sp, next) => serviceB); services.AddService(); services.AddSingleton(new ServiceB()); - - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -130,6 +110,7 @@ private class TestApiC : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); var q1 = new ServiceB("q1Pre", "q1Post"); var q2 = new ServiceB("q2Pre", "q2Post"); services.AddService((sp, next) => q1) @@ -139,12 +120,6 @@ private class TestApiC : ApiBase return q2; }); - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); - return services; } diff --git a/test/Microsoft.Restier.Core.Tests/ApiContext.Tests.cs b/test/Microsoft.Restier.Core.Tests/ApiContext.Tests.cs index 060398b0..2945a8f6 100644 --- a/test/Microsoft.Restier.Core.Tests/ApiContext.Tests.cs +++ b/test/Microsoft.Restier.Core.Tests/ApiContext.Tests.cs @@ -24,7 +24,7 @@ public void NewApiContextIsConfiguredCorrectly() var api = provider.GetService(); var context = api.Context; - Assert.NotNull(context.Configuration); + Assert.NotNull(context); } } } diff --git a/test/Microsoft.Restier.Core.Tests/InvocationContext.Tests.cs b/test/Microsoft.Restier.Core.Tests/InvocationContext.Tests.cs index fb760933..cb9c4b0c 100644 --- a/test/Microsoft.Restier.Core.Tests/InvocationContext.Tests.cs +++ b/test/Microsoft.Restier.Core.Tests/InvocationContext.Tests.cs @@ -28,12 +28,7 @@ public static ApiServiceA ApiService public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); - + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => ApiService); return services; diff --git a/test/Microsoft.Restier.Core.Tests/Model/DefaultModelHandler.Tests.cs b/test/Microsoft.Restier.Core.Tests/Model/DefaultModelHandler.Tests.cs index ef9e4861..82859b35 100644 --- a/test/Microsoft.Restier.Core.Tests/Model/DefaultModelHandler.Tests.cs +++ b/test/Microsoft.Restier.Core.Tests/Model/DefaultModelHandler.Tests.cs @@ -19,6 +19,7 @@ private class TestApiA : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => new TestModelProducer()); services.AddService((sp, next) => new TestModelExtender(2) { @@ -29,12 +30,6 @@ private class TestApiA : ApiBase InnerHandler = next, }); - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); - return services; } @@ -47,14 +42,9 @@ private class TestApiB : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); var service = new TestSingleCallModelBuilder(); services.AddService((sp, next) => service); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -67,15 +57,10 @@ private class TestApiC : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); var service = new TestRetryModelBuilder(); services.AddService((sp, next) => service); - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); - return services; } diff --git a/test/Microsoft.Restier.Core.Tests/ServiceConfiguration.Tests.cs b/test/Microsoft.Restier.Core.Tests/ServiceConfiguration.Tests.cs index cb07e400..c1689f19 100644 --- a/test/Microsoft.Restier.Core.Tests/ServiceConfiguration.Tests.cs +++ b/test/Microsoft.Restier.Core.Tests/ServiceConfiguration.Tests.cs @@ -14,6 +14,7 @@ private class TestApiA : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); var i = 0; services.AddService((sp, next) => new SomeService { @@ -36,12 +37,6 @@ private class TestApiA : ApiBase Value = i++ }) .AddService(); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -54,6 +49,7 @@ private class TestApiB : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => new SomeService { Inner = next, @@ -61,12 +57,6 @@ private class TestApiB : ApiBase }) .AddService() .MakeTransient(); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -79,14 +69,9 @@ private class TestApiC : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.MakeScoped() .AddService((sp, next) => new SomeService()); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -99,6 +84,7 @@ private class TestApiD : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => new SomeService { Inner = next, @@ -106,12 +92,6 @@ private class TestApiD : ApiBase }) .AddService() .MakeTransient(); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -124,6 +104,7 @@ private class TestApiE : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); var first = new SomeService { Value = 42 @@ -132,12 +113,6 @@ private class TestApiE : ApiBase .AddService((sp, next) => first) .AddService() .AddSingleton("Text"); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -150,6 +125,7 @@ private class TestApiF : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => new SomeService { Value = 2 @@ -157,12 +133,6 @@ private class TestApiF : ApiBase .MakeTransient() .AddService(); - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); - return services; } @@ -175,6 +145,7 @@ private class TestApiG : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => new SomeService { Value = 1 @@ -187,13 +158,6 @@ private class TestApiG : ApiBase .MakeTransient() .AddService((sp, next) => { return "0"; }) .MakeTransient(); - - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -206,6 +170,7 @@ private class TestApiH : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.AddService((sp, next) => new SomeService { Value = 1 @@ -214,12 +179,6 @@ private class TestApiH : ApiBase .MakeTransient() .AddService((sp, next) => { return "0"; }) .MakeTransient(); - - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } @@ -232,6 +191,7 @@ private class TestApiI : ApiBase { public static new IServiceCollection ConfigureApi(Type apiType, IServiceCollection services) { + ApiBase.ConfigureApi(apiType, services); services.MakeTransient() .AddService((sp, next) => new SomeService { @@ -243,11 +203,6 @@ private class TestApiI : ApiBase }) .AddService(); - services.AddScoped(apiType, apiType) - .AddScoped(typeof(ApiBase), apiType) - .AddScoped(); - - services.TryAddSingleton(); return services; } diff --git a/test/Microsoft.Restier.Publishers.OData.Test/FallbackTests.cs b/test/Microsoft.Restier.Publishers.OData.Test/FallbackTests.cs index 124eea0e..fe4daaa6 100644 --- a/test/Microsoft.Restier.Publishers.OData.Test/FallbackTests.cs +++ b/test/Microsoft.Restier.Publishers.OData.Test/FallbackTests.cs @@ -108,7 +108,7 @@ internal class FallbackApi : ApiBase [Resource] public IQueryable PreservedOrders { - get { return this.GetQueryableSource("Orders").Where(o => o.Id > 123); } + get { return this.Context.GetQueryableSource("Orders").Where(o => o.Id > 123); } } public FallbackApi(IServiceProvider serviceProvider) : base(serviceProvider) diff --git a/test/Microsoft.Restier.Publishers.OData.Test/Model/RestierModelExtender.Tests.cs b/test/Microsoft.Restier.Publishers.OData.Test/Model/RestierModelExtender.Tests.cs index 5211a707..cf71001b 100644 --- a/test/Microsoft.Restier.Publishers.OData.Test/Model/RestierModelExtender.Tests.cs +++ b/test/Microsoft.Restier.Publishers.OData.Test/Model/RestierModelExtender.Tests.cs @@ -179,11 +179,6 @@ public Task GetModelAsync(ModelContext context, CancellationToken can public class BaseApi : ApiBase { - public ApiConfiguration ApiConfiguration - { - get { return base.Configuration; } - } - public BaseApi(IServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/test/Microsoft.Restier.TestCommon/PublicApi.bsl b/test/Microsoft.Restier.TestCommon/PublicApi.bsl index 5b294c9b..0ad80fa0 100644 --- a/test/Microsoft.Restier.TestCommon/PublicApi.bsl +++ b/test/Microsoft.Restier.TestCommon/PublicApi.bsl @@ -1,7 +1,6 @@ public abstract class Microsoft.Restier.Core.ApiBase : IDisposable { protected ApiBase (System.IServiceProvider serviceProvider) - Microsoft.Restier.Core.ApiConfiguration Configuration { public get; } Microsoft.Restier.Core.ApiContext Context { public get; } bool IsDisposed { [CompilerGeneratedAttribute(),]public get; } System.IServiceProvider ServiceProvider { public get; } @@ -9,49 +8,18 @@ public abstract class Microsoft.Restier.Core.ApiBase : IDisposable { [ CLSCompliantAttribute(), ] - public static Microsoft.Extensions.DependencyInjection.IServiceCollection ConfigureApi (System.Type apiType, Microsoft.Extensions.DependencyInjection.IServiceCollection services) - - public virtual void Dispose () -} - -[ -ExtensionAttribute(), -] -public sealed class Microsoft.Restier.Core.ApiBaseExtensions { - [ - ExtensionAttribute(), - ] - public static System.Threading.Tasks.Task`1[[Microsoft.OData.Edm.IEdmModel]] GetModelAsync (Microsoft.Restier.Core.ApiBase api, params System.Threading.CancellationToken cancellationToken) - - [ - ExtensionAttribute(), - ] - public static System.Linq.IQueryable GetQueryableSource (Microsoft.Restier.Core.ApiBase api, string name, object[] arguments) - - [ - ExtensionAttribute(), - ] - public static IQueryable`1 GetQueryableSource (Microsoft.Restier.Core.ApiBase api, string name, object[] arguments) - - [ - ExtensionAttribute(), - ] - public static System.Linq.IQueryable GetQueryableSource (Microsoft.Restier.Core.ApiBase api, string namespaceName, string name, object[] arguments) - - [ - ExtensionAttribute(), - ] - public static IQueryable`1 GetQueryableSource (Microsoft.Restier.Core.ApiBase api, string namespaceName, string name, object[] arguments) + public static void AddPublisherServices (System.Type apiType, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configurationCallback) [ - ExtensionAttribute(), + CLSCompliantAttribute(), ] - public static System.Threading.Tasks.Task`1[[Microsoft.Restier.Core.Query.QueryResult]] QueryAsync (Microsoft.Restier.Core.ApiBase api, Microsoft.Restier.Core.Query.QueryRequest request, params System.Threading.CancellationToken cancellationToken) + public static Microsoft.Extensions.DependencyInjection.IServiceCollection ConfigureApi (System.Type apiType, Microsoft.Extensions.DependencyInjection.IServiceCollection services) + public virtual void Dispose () [ - ExtensionAttribute(), + CLSCompliantAttribute(), ] - public static System.Threading.Tasks.Task`1[[Microsoft.Restier.Core.Submit.SubmitResult]] SubmitAsync (Microsoft.Restier.Core.ApiBase api, params Microsoft.Restier.Core.Submit.ChangeSet changeSet, params System.Threading.CancellationToken cancellationToken) + public static System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] GetPublisherServiceCallback (System.Type apiType) } [ @@ -199,24 +167,12 @@ public sealed class Microsoft.Restier.Core.ServiceCollectionExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection MakeTransient (Microsoft.Extensions.DependencyInjection.IServiceCollection services) } -public class Microsoft.Restier.Core.ApiConfiguration { - public ApiConfiguration () - - [ - CLSCompliantAttribute(), - ] - public static void AddPublisherServices (System.Type apiType, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configurationCallback) - - [ - CLSCompliantAttribute(), - ] - public static System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] GetPublisherServiceCallback (System.Type apiType) -} +public class Microsoft.Restier.Core.ApiContext : IDisposable { + public ApiContext (System.IServiceProvider provider) -public class Microsoft.Restier.Core.ApiContext { - public ApiContext (System.IServiceProvider provider, Microsoft.Restier.Core.ApiConfiguration configuration) + bool IsDisposed { [CompilerGeneratedAttribute(),]public get; } - Microsoft.Restier.Core.ApiConfiguration Configuration { [CompilerGeneratedAttribute(),]public get; } + public virtual void Dispose () } public class Microsoft.Restier.Core.InvocationContext { @@ -283,7 +239,7 @@ public interface Microsoft.Restier.Core.Operation.IOperationAuthorizer { } public interface Microsoft.Restier.Core.Operation.IOperationExecutor { - System.Threading.Tasks.Task`1[[System.Linq.IQueryable]] ExecuteOperationAsync (object instanceImplementMethod, Microsoft.Restier.Core.Operation.OperationContext context, System.Threading.CancellationToken cancellationToken) + System.Threading.Tasks.Task`1[[System.Linq.IQueryable]] ExecuteOperationAsync (Microsoft.Restier.Core.Operation.OperationContext context, System.Threading.CancellationToken cancellationToken) } public interface Microsoft.Restier.Core.Operation.IOperationFilter { diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/QueryTests.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/QueryTests.cs index 6415fb8e..5901f864 100644 --- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/QueryTests.cs +++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/QueryTests.cs @@ -25,7 +25,7 @@ public async Task TestTakeIncludeTotalCount() { WebApiConfig.RegisterNorthwind(config, server); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test"); request.SetConfiguration(config); - var api = request.CreateRequestContainer("NorthwindApi").GetService(); + var api = request.CreateRequestContainer("NorthwindApi").GetService(); QueryResult result = await api.QueryAsync( new QueryRequest(api.GetQueryableSource("Orders").OrderBy(o => o.OrderDate).Take(10))); @@ -45,7 +45,7 @@ public async Task TestSkipIncludeTotalCount() WebApiConfig.RegisterNorthwind(config, server); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test"); request.SetConfiguration(config); - var api = request.CreateRequestContainer("NorthwindApi").GetService(); + var api = request.CreateRequestContainer("NorthwindApi").GetService(); QueryResult result = await api.QueryAsync( new QueryRequest(api.GetQueryableSource("Orders").OrderBy(o => o.OrderDate).Skip(10))); @@ -65,7 +65,7 @@ public async Task TestSkipTakeIncludeTotalCount() WebApiConfig.RegisterNorthwind(config, server); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test"); request.SetConfiguration(config); - var api = request.CreateRequestContainer("NorthwindApi").GetService(); + var api = request.CreateRequestContainer("NorthwindApi").GetService(); QueryResult result = await api.QueryAsync( new QueryRequest(api.GetQueryableSource("Orders").OrderBy(o => o.OrderDate).Skip(10).Take(25))); @@ -89,7 +89,7 @@ public async Task TestTakeNotStrippedIncludeTotalCount() WebApiConfig.RegisterNorthwind(config, server); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test"); request.SetConfiguration(config); - var api = request.CreateRequestContainer("NorthwindApi").GetService(); + var api = request.CreateRequestContainer("NorthwindApi").GetService(); QueryResult result = await api.QueryAsync( new QueryRequest(api.GetQueryableSource("Orders").Take(10).OrderBy(o => o.OrderDate))); diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/SaveTests.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/SaveTests.cs index 0c5521e1..98306a45 100644 --- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/SaveTests.cs +++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind.Tests/SaveTests.cs @@ -59,7 +59,7 @@ public async Task TestEntityFilterReturnsTask() { var container = new RestierContainerBuilder(typeof(TestEntityFilterReturnsTaskApi)); var provider = container.BuildContainer(); - var api = provider.GetService(); + var api = provider.GetService(); DataModificationItem createCustomer = new DataModificationItem( "Customers", diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Controllers/NorthwindController.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Controllers/NorthwindController.cs index 155c33a9..8331ada0 100644 --- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Controllers/NorthwindController.cs +++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Controllers/NorthwindController.cs @@ -27,7 +27,7 @@ private NorthwindContext DbContext get { var api =(NorthwindApi)this.Request.GetRequestContainer().GetService(); - return api.Context; + return api.ModelContext; } } diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Models/NorthwindApi.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Models/NorthwindApi.cs index 720219d6..4a2b6eca 100644 --- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Models/NorthwindApi.cs +++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Northwind/Models/NorthwindApi.cs @@ -19,7 +19,7 @@ namespace Microsoft.OData.Service.Sample.Northwind.Models { public class NorthwindApi : EntityFrameworkApi { - public new NorthwindContext Context { get { return DbContext; } } + public NorthwindContext ModelContext { get { return DbContext; } } // Imperative views. Currently CUD operations not supported [Resource] @@ -27,7 +27,7 @@ public IQueryable ExpensiveProducts { get { - return this.GetQueryableSource("Products") + return this.Context.GetQueryableSource("Products") .Where(c => c.UnitPrice > 50); } } @@ -37,7 +37,7 @@ public IQueryable CurrentOrders { get { - return this.GetQueryableSource("Orders") + return this.Context.GetQueryableSource("Orders") .Where(o => o.ShippedDate == null); } } diff --git a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Api/TrippinApi.cs b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Api/TrippinApi.cs index a44f1841..8007dfa7 100644 --- a/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Api/TrippinApi.cs +++ b/test/ODataEndToEnd/Microsoft.OData.Service.Sample.Trippin/Api/TrippinApi.cs @@ -52,7 +52,7 @@ public IQueryable Flights1 [Resource] public IQueryable Flights2 { - get { return this.GetQueryableSource("Flights"); } + get { return this.Context.GetQueryableSource("Flights"); } } [Resource] @@ -75,7 +75,7 @@ public IQueryable PeopleWithAge1 { get { - return this.GetQueryableSource("People").Select(p => new PersonWithAge + return this.Context.GetQueryableSource("People").Select(p => new PersonWithAge { Id = p.PersonId, UserName = p.UserName,