/// <summary> /// This method processes a single SyncInterceptorAttribute defined on a method. Processing involves the following /// 1. Ensure that the MethodInfo signature is the right one for the interceptor. /// 2. Retrieve the ScopeNames defined in the attribute and ensure they are valid scopes configures via the /// ISyncScopeConfiguration.SetEnableScope() API. /// 3. Create a SyncInterceptorInfoWrapper object for the scope if none is present. /// 4. Add the interceptor to the wrapper object. /// </summary> /// <param name="attr">The SyncInterceptorAttribute to process.</param> /// <param name="syncServiceType">Actual SyncService type</param> /// <param name="methodInfo">User Method on which the attribute is applied</param> private void ProcessSyncInterceptor(SyncInterceptorAttribute attr, Type syncServiceType, MethodInfo methodInfo) { // Validate the method signature attribute WebUtil.ValidateInterceptorSignature(attr, methodInfo, syncServiceType.Name); // Read the list of scopeNames from the attribute string[] scopeNames = attr.ScopeName.Select(e => e.ToLowerInvariant()).ToArray(); foreach (string scopeName in scopeNames) { // Check to ensure the scopeName is valid configured scope. if (!this.ScopeNames.Contains(scopeName)) { // ScopeName is not part of configured scopes. Throw. throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "ScopeName '{0}' defined in '{1}' on method '{2}' is not in the list of configured sync scopes.", scopeName, attr.GetType().Name, methodInfo.Name)); } SyncInterceptorsInfoWrapper wrapper = null; // Check and create the wrapper object for the current scope if none exists. if (!this.SyncInterceptors.TryGetValue(scopeName, out wrapper)) { wrapper = new SyncInterceptorsInfoWrapper(scopeName); this.SyncInterceptors.Add(scopeName, wrapper); } // Add interceptor to the wrapper. wrapper.AddInterceptor(attr, methodInfo, syncServiceType.Name); } }
/// <summary> /// Checks to see if a typed SyncResponseInterceptor for a specific operation has been configured by the user /// </summary> /// <returns>bool</returns> internal bool HasTypedResponseInterceptor(string scopeName, SyncOperations operation, Type type) { SyncInterceptorsInfoWrapper wrapper = null; if (this.SyncInterceptors.TryGetValue(scopeName.ToLowerInvariant(), out wrapper)) { return(wrapper.HasResponseInterceptor(operation, type)); } return(false); }
/// <summary> /// Checks to see if a typed SyncConflictInterceptor has been configured by the user /// </summary> /// <returns>bool</returns> internal bool HasTypedConflictInterceptor(string scopeName, Type type) { SyncInterceptorsInfoWrapper wrapper = null; if (this.SyncInterceptors.TryGetValue(scopeName.ToLowerInvariant(), out wrapper)) { return(wrapper.HasConflictInterceptor(type)); } return(false); }
/// <summary> /// Utility function that invokes the actual user interceptor extension method. If entityType is null /// it looks for the generic interceptor. If its not null then it looks for a typed interceptor for the /// specific type being passed. /// </summary> /// <param name="context">The context to pass as parameter to user code</param> /// <param name="entityType">Type of the entity being processed</param> /// <param name="isRequest">True if intercepting a request operation else false.</param> internal void InvokeOperationInterceptors(SyncOperationContext context, Type entityType, bool isRequest) { SyncInterceptorsInfoWrapper wrapper = null; if (this.SyncInterceptors.TryGetValue(context.ScopeName, out wrapper)) { MethodInfo methodInfo = null; switch (context.Operation) { case SyncOperations.Download: if (entityType == null) { methodInfo = (isRequest) ? wrapper.DownloadRequestInterceptor : wrapper.DownloadResponseInterceptor; } else { Debug.Assert(!isRequest, "Cannot fire typed interceptor for DownloadRequest"); methodInfo = wrapper.GetResponseInterceptor(SyncOperations.Download, entityType); } break; case SyncOperations.Upload: if (entityType == null) { methodInfo = (isRequest) ? wrapper.UploadRequestInterceptor : wrapper.UploadResponseInterceptor; } else { methodInfo = (isRequest) ? wrapper.GetRequestInterceptor(entityType) : wrapper.GetResponseInterceptor(SyncOperations.Upload, entityType); } break; } if (methodInfo != null) { InvokeUserInterceptorMethod(methodInfo, OperationContext.Current.InstanceContext.GetServiceInstance(), new object[] { context }); } } }
/// <summary> /// Utility for invoking user code for conflict interceptors /// </summary> /// <param name="context">The context to pass as parameter to user code</param> /// <param name="mergedVersion">The merged version for Merge resolution</param> /// <param name="entityType">Entity type of the conflict being raised</param> /// <returns>Actual resolution picked by user</returns> internal SyncConflictResolution?InvokeConflictInterceptor(SyncConflictContext context, Type entityType, out IOfflineEntity mergedVersion) { SyncInterceptorsInfoWrapper wrapper = null; if (this.SyncInterceptors.TryGetValue(context.ScopeName, out wrapper)) { // Look for unfiltered Conflict and if that is null then look for filtered one. // Its an error to have both unfiltered and filtered ConflictInterceptor so both cannot be set. MethodInfo methodInfo = wrapper.ConflictInterceptor ?? wrapper.GetConflictInterceptor(entityType); if (methodInfo != null) { object[] inputParams = new object[] { context, null }; SyncConflictResolution resolution = (SyncConflictResolution)InvokeUserInterceptorMethod(methodInfo, OperationContext.Current.InstanceContext.GetServiceInstance(), inputParams); // Merged version is in the second parameter which is passed by reference. Look it up mergedVersion = (IOfflineEntity)inputParams[1]; return(resolution); } } mergedVersion = null; return(null); }