public ChangeSetEntry(object entity, DomainOperation operation, bool returnEntity = true, EntityUpdateFiledsInfo entityUpdateFiledsInfo = null) : this(0, entity, null, operation, returnEntity, entityUpdateFiledsInfo) { }
/// <summary> /// Invokes an operation according to the specified <paramref name="operationType"/> and entity /// </summary> /// <typeparam name="TEntity">The entity type</typeparam> /// <param name="operationType">The type of operation to invoke</param> /// <param name="submitOperation"> /// The <see cref="Expression"/> identifying the operation to invoke. This parameter can be <c>null</c> /// as long as <paramref name="entity"/> is not. /// </param> /// <param name="entity"> /// The entity to pass to the operation. This parameter can be <c>null</c> as long as /// <paramref name="submitOperation"/> is not. /// </param> /// <param name="original">The original version of the entity. This parameter can be <c>null</c>.</param> /// <exception cref="DomainServiceTestHostException">is thrown if there are any <see cref="ChangeSet"/> errors</exception> private void SubmitCore <TEntity>(DomainOperation operationType, Expression submitOperation, TEntity entity, TEntity original) where TEntity : class { OperationContext context = this.CreateOperationContext(DomainOperationType.Submit); ChangeSetEntry changeSetEntry; if (operationType == DomainOperation.Update) { if (submitOperation != null) { changeSetEntry = Utility.GetCustomUpdateChangeSetEntry(context, submitOperation, original); } else { changeSetEntry = Utility.GetChangeSetEntry(context, entity, original, operationType); changeSetEntry.HasMemberChanges = true; } } else { changeSetEntry = Utility.GetChangeSetEntry(context, entity, original, operationType); } ChangeSet changeSet = Utility.CreateChangeSet(changeSetEntry); SubmitChangeSetCore(context, changeSet); }
/// <summary> /// Invokes an operation according to the specified <paramref name="operationType"/> and entity and returns /// the validation errors, the change set, and whether the operation completed successfully /// </summary> /// <typeparam name="TEntity">The entity type</typeparam> /// <param name="operationType">The type of operation to invoke</param> /// <param name="submitOperation"> /// The <see cref="Expression"/> identifying the operation to invoke. This parameter can be <c>null</c> /// as long as <paramref name="entity"/> is not. /// </param> /// <param name="entity"> /// The entity to pass to the operation. This parameter can be <c>null</c> as long as /// <paramref name="submitOperation"/> is not. /// </param> /// <param name="original">The original version of the entity. This parameter can be <c>null</c>.</param> /// <param name="validationResults">The validation errors that occurred</param> /// <param name="changeSet">The change set</param> private bool TrySubmitCore <TEntity>(DomainOperation operationType, Expression submitOperation, TEntity entity, TEntity original, out IList <ValidationResult> validationResults, out ChangeSet changeSet) where TEntity : class { OperationContext context = this.CreateOperationContext(DomainOperationType.Submit); ChangeSetEntry changeSetEntry; if (operationType == DomainOperation.Update) { if (submitOperation != null) { changeSetEntry = Utility.GetCustomUpdateChangeSetEntry(context, submitOperation, original); } else { changeSetEntry = Utility.GetChangeSetEntry(context, entity, original, operationType); changeSetEntry.HasMemberChanges = true; } } else { changeSetEntry = Utility.GetChangeSetEntry(context, entity, original, operationType); } changeSet = Utility.CreateChangeSet(changeSetEntry); return(TrySubmitChangeSetCore(context, changeSet, out validationResults)); }
/// <summary> /// Initializes a new <see cref="DomainDataServiceOperation"/> instance. /// </summary> /// <param name="name">name of the service operation.</param> /// <param name="resultKind">Kind of result expected from this operation.</param> /// <param name="resultType">Type of element of the method result.</param> /// <param name="resultSet">EntitySet of the result expected from this operation.</param> /// <param name="method">Protocol (for example HTTP) method the service operation responds to.</param> /// <param name="parameters">In-order parameters for this operation.</param> /// <param name="operationKind">Kind of domain service operation.</param> internal DomainDataServiceOperation( string name, ServiceOperationResultKind resultKind, ResourceType resultType, ResourceSet resultSet, string method, IEnumerable <ServiceOperationParameter> parameters, DomainOperation operationKind) : base(name, resultKind, resultType, resultSet, method, parameters) { this.OperationKind = operationKind; }
/// <summary> /// Initializes a new instance of the <see cref="ChangeSetEntry"/> class /// </summary> /// <param name="id">The client Id for the entity.</param> /// <param name="entity">The entity.</param> /// <param name="originalEntity">The original entity. May be null.</param> /// <param name="operation">The operation to be performed</param> public ChangeSetEntry(int id, object entity, object originalEntity, DomainOperation operation) { if (entity == null) { throw new ArgumentNullException(nameof(entity)); } this._id = id; this._entity = entity; // Setting original entity through a property so that _hasMemberChanges is set to correct value this.OriginalEntity = originalEntity; this.Operation = operation; }
/// <summary> /// Initializes a new instance of the <see cref="ChangeSetEntry"/> class /// </summary> /// <param name="id">The client Id for the entity.</param> /// <param name="entity">The entity.</param> /// <param name="originalEntity">The original entity. May be null.</param> /// <param name="operation">The operation to be performed</param> public ChangeSetEntry(int id, object entity, object originalEntity, DomainOperation operation) { if (entity == null) { throw new ArgumentNullException("entity"); } this._id = id; this._entity = entity; // Setting original entity through a property so that _hasMemberChanges is set to correct value this.OriginalEntity = originalEntity; this.Operation = operation; }
/// <summary> /// Initializes a new instance of the DomainOperationEntry class /// </summary> /// <param name="domainServiceType">The <see cref="DomainService"/> Type this operation is a member of.</param> /// <param name="name">The name of the operation</param> /// <param name="operation">The <see cref="DomainOperation"/></param> /// <param name="returnType">The return Type of the operation</param> /// <param name="parameters">The parameter definitions for the operation</param> /// <param name="attributes">The method level attributes for the operation</param> protected DomainOperationEntry(Type domainServiceType, string name, DomainOperation operation, Type returnType, IEnumerable <DomainOperationParameter> parameters, AttributeCollection attributes) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (attributes == null) { throw new ArgumentNullException(nameof(attributes)); } if (domainServiceType == null) { throw new ArgumentNullException(nameof(domainServiceType)); } if (operation == DomainOperation.None) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainOperationEntryType, Enum.GetName(typeof(DomainOperation), operation))); } bool isTaskType = TypeUtility.IsTaskType(returnType); this._methodName = isTaskType ? RemoveAsyncFromName(name) : name; this._actualReturnType = returnType; this.ReturnType = isTaskType ? TypeUtility.GetTaskReturnType(returnType) : returnType; this._attributes = attributes; this.Operation = operation; this._domainServiceType = domainServiceType; List <DomainOperationParameter> effectiveParameters = parameters.ToList(); int paramCount = effectiveParameters.Count; if (paramCount > 0) { DomainOperationParameter lastParameter = effectiveParameters[paramCount - 1]; if (lastParameter.IsOut && lastParameter.ParameterType.HasElementType && lastParameter.ParameterType.GetElementType() == typeof(int)) { this.HasOutCountParameter = true; effectiveParameters = effectiveParameters.Take(paramCount - 1).ToList(); } } this.Parameters = effectiveParameters.AsReadOnly(); }
private DomainOperation insertDomainOperation(DomainOperation type, TreeNode parent) { DomainOperation n = new DomainOperation(type); if (parent == null) { tree.Nodes.Add(n.TreeNode); } else { parent.Nodes.Add(n.TreeNode); } return(n); }
public bool Load(string nodesXMLFilename) { try { XElement x = XElement.Load(nodesXMLFilename, LoadOptions.None); XElement settings = x.Element("settings"); foreach (XElement n in x.Elements("node")) { switch (n.Attribute("type").Value) { case "do": DistancePrimitive dp = new DistancePrimitive(n.Attribute("name").Value, n.Attribute("caption").Value, n.Attribute("code").Value); foreach (XElement p in n.Elements("prop")) { dp.Properties.Add(createInputProperty(p.Attribute("type").Value, p.Attribute("name").Value, p.Attribute("default").Value)); } DistanceFieldTypes.Add(dp); break; case "distop": DistanceOperation distop = new DistanceOperation(n.Attribute("name").Value, n.Attribute("caption").Value, n.Attribute("code").Value); foreach (XElement p in n.Elements("prop")) { distop.Properties.Add(createInputProperty(p.Attribute("type").Value, p.Attribute("name").Value, p.Attribute("default").Value)); } DistanceOperations.Add(distop); break; case "domop": DomainOperation dop = new DomainOperation(n.Attribute("name").Value, n.Attribute("caption").Value, n.Attribute("code").Value); foreach (XElement p in n.Elements("prop")) { dop.Properties.Add(createInputProperty(p.Attribute("type").Value, p.Attribute("name").Value, p.Attribute("default").Value)); } DomainOperations.Add(dop); break; } } } catch (Exception e) { errors.Add("Failed to load nodes.xml (" + e.Message + ")"); return(false); } return(true); }
public ChangeSetEntry(int id, object entity, object originalEntity, DomainOperation operation, bool returnEntity = true, EntityUpdateFiledsInfo entityUpdateFiledsInfo = null) { if (entity == null) { throw new ArgumentNullException("entity"); } this._id = id; this._entity = entity; this.OriginalEntity = originalEntity; this.Operation = operation; ReturnEntity = returnEntity; EntityUpdateFiledsInfo = entityUpdateFiledsInfo; }
/// <summary> /// Classifies a domain operation based on naming convention. /// </summary> /// <param name="operation">The domain operation to inspect.</param> /// <returns>True if the operation matches a convention; false otherwise.</returns> private bool TryClassifyImplicitDomainOperation(ReflectionDomainOperationEntry operation) { DomainOperation operationType = (DomainOperation)(-1); if (operation.ReturnType == typeof(void)) { // Check if this looks like an insert, update or delete method. if (insertPrefixes.Any(p => operation.Name.StartsWith(p, StringComparison.OrdinalIgnoreCase))) { operationType = DomainOperation.Insert; } else if (updatePrefixes.Any(p => operation.Name.StartsWith(p, StringComparison.OrdinalIgnoreCase))) { operationType = DomainOperation.Update; } else if (deletePrefixes.Any(p => operation.Name.StartsWith(p, StringComparison.OrdinalIgnoreCase))) { operationType = DomainOperation.Delete; } else if (this.IsCustomMethod(operation)) { operationType = DomainOperation.Custom; } } else if (this.IsQueryMethod(operation)) { operationType = DomainOperation.Query; } if ((int)operationType == -1 && IsInvokeOperation(operation)) { operationType = DomainOperation.Invoke; } if ((int)operationType != -1) { operation.Operation = operationType; operation.IsInferred = true; return(true); } return(false); }
/// <summary> /// Helper method performs a submit operation against a given proxy instance. /// </summary> /// <param name="domainService">The type of <see cref="DomainService"/> to perform this query operation against.</param> /// <param name="context">The current context.</param> /// <param name="domainServiceInstances">The list of tracked <see cref="DomainService"/> instances that any newly created /// <see cref="DomainServices"/> will be added to.</param> /// <param name="currentOriginalEntityMap">The mapping of current and original entities used with the utility <see cref="DomainServiceProxy.AssociateOriginal"/> method.</param> /// <param name="entity">The entity being submitted.</param> /// <param name="operationName">The name of the submit operation. For CUD operations, this can be null.</param> /// <param name="parameters">The submit operation parameters.</param> /// <param name="domainOperation">The type of submit operation.</param> /// <exception cref="ArgumentNullException">if <paramref name="context"/> is null.</exception> /// <exception cref="ArgumentNullException">if <paramref name="entity"/> is null.</exception> /// <exception cref="OperationException">if operation errors are thrown during execution of the submit operation.</exception> public static void Submit(Type domainService, DomainServiceContext context, IList<DomainService> domainServiceInstances, IDictionary<object, object> currentOriginalEntityMap, object entity, string operationName, object[] parameters, DomainOperation domainOperation) { context = new DomainServiceContext(context, DomainOperationType.Submit); DomainService service = CreateDomainServiceInstance(domainService, context, domainServiceInstances); object originalEntity = null; currentOriginalEntityMap.TryGetValue(entity, out originalEntity); // if this is an update operation, regardless of whether original // values have been specified, we need to mark the operation as // modified bool hasMemberChanges = domainOperation == DomainOperation.Update; // when custom methods are invoked, the operation type // is Update if (domainOperation == DomainOperation.Custom) { domainOperation = DomainOperation.Update; } ChangeSetEntry changeSetEntry = new ChangeSetEntry(1, entity, originalEntity, domainOperation); changeSetEntry.HasMemberChanges = hasMemberChanges; if (!string.IsNullOrEmpty(operationName)) { changeSetEntry.EntityActions = new List<Serialization.KeyValue<string, object[]>>(); changeSetEntry.EntityActions.Add(new Serialization.KeyValue<string, object[]>(operationName, parameters)); } ChangeSet changeSet = new ChangeSet(new[] { changeSetEntry }); service.Submit(changeSet); if (changeSetEntry.HasError) { throw new OperationException(Resource.DomainServiceProxy_OperationError, changeSetEntry.ValidationErrors); } }
/// <summary> /// Helper method performs a submit operation against a given proxy instance. /// </summary> /// <param name="domainService">The type of <see cref="DomainService"/> to perform this query operation against.</param> /// <param name="context">The current context.</param> /// <param name="domainServiceInstances">The list of tracked <see cref="DomainService"/> instances that any newly created /// <see cref="DomainServices"/> will be added to.</param> /// <param name="currentOriginalEntityMap">The mapping of current and original entities used with the utility <see cref="DomainServiceProxy.AssociateOriginal"/> method.</param> /// <param name="entity">The entity being submitted.</param> /// <param name="operationName">The name of the submit operation. For CUD operations, this can be null.</param> /// <param name="parameters">The submit operation parameters.</param> /// <param name="domainOperation">The type of submit operation.</param> /// <exception cref="ArgumentNullException">if <paramref name="context"/> is null.</exception> /// <exception cref="ArgumentNullException">if <paramref name="entity"/> is null.</exception> /// <exception cref="OperationException">if operation errors are thrown during execution of the submit operation.</exception> public static void Submit(Type domainService, DomainServiceContext context, IList <DomainService> domainServiceInstances, IDictionary <object, object> currentOriginalEntityMap, object entity, string operationName, object[] parameters, DomainOperation domainOperation) { context = new DomainServiceContext(context, DomainOperationType.Submit); DomainService service = CreateDomainServiceInstance(domainService, context, domainServiceInstances); object originalEntity = null; currentOriginalEntityMap.TryGetValue(entity, out originalEntity); // if this is an update operation, regardless of whether original // values have been specified, we need to mark the operation as // modified bool hasMemberChanges = domainOperation == DomainOperation.Update; // when custom methods are invoked, the operation type // is Update if (domainOperation == DomainOperation.Custom) { domainOperation = DomainOperation.Update; } ChangeSetEntry changeSetEntry = new ChangeSetEntry(1, entity, originalEntity, domainOperation); changeSetEntry.HasMemberChanges = hasMemberChanges; if (!string.IsNullOrEmpty(operationName)) { changeSetEntry.EntityActions = new List <Serialization.KeyValue <string, object[]> >(); changeSetEntry.EntityActions.Add(new Serialization.KeyValue <string, object[]>(operationName, parameters)); } ChangeSet changeSet = new ChangeSet(new[] { changeSetEntry }); service.SubmitAsync(changeSet, CancellationToken.None) .GetAwaiter().GetResult(); if (changeSetEntry.HasError) { throw new OperationException(Resource.DomainServiceProxy_OperationError, changeSetEntry.ValidationErrors); } }
public static ChangeSetEntry GetChangeSetEntry(OperationContext context, object entity, object original, DomainOperation operationType) { Utility.EnsureOperationSupported(context, entity.GetType(), operationType); context.OperationName = context.DomainServiceDescription.GetSubmitMethod(entity.GetType(), operationType).Name; return new ChangeSetEntry(Utility.DefaultChangeSetEntryId, entity, original, operationType); }
/// <summary> /// Creates an instance of a <see cref="ReflectionDomainOperationEntry"/>. /// </summary> /// <param name="domainServiceType">The DomainService Type the method belongs to.</param> /// <param name="methodInfo">The MethodInfo of the method.</param> /// <param name="operation">The operation.</param> public ReflectionDomainOperationEntry(Type domainServiceType, MethodInfo methodInfo, DomainOperation operation) : base(domainServiceType, methodInfo.Name, operation, methodInfo.ReturnType, GetMethodParameters(methodInfo), GetAttributeCollection(methodInfo)) { if (methodInfo == null) { throw new ArgumentNullException("methodInfo"); } // Generic methods aren’t supported, and will be caught during DomainServiceDescription validation. if (!methodInfo.IsGenericMethodDefinition) { this._method = DynamicMethodUtility.GetDelegateForMethod(methodInfo); } }
/// <summary> /// Gets the submit method for the specified entity type and <see cref="DomainOperation"/> /// </summary> /// <param name="entityType">The entity type</param> /// <param name="operation">The <see cref="DomainOperation"/> type to get the method for. Must be /// an Insert, Update or Delete operation.</param> /// <returns>The method if it exists, otherwise null</returns> public DomainOperationEntry GetSubmitMethod(Type entityType, DomainOperation operation) { this.EnsureInitialized(); if (entityType == null) { throw new ArgumentNullException("entityType"); } if ((operation != DomainOperation.Insert) && (operation != DomainOperation.Update) && (operation != DomainOperation.Delete)) { throw new ArgumentOutOfRangeException("operation"); } for (Type baseType = entityType; baseType != typeof(object) && baseType != null; baseType = baseType.BaseType) { Dictionary<DomainOperation, DomainOperationEntry> entitySubmitMethods = null; if (this._submitMethods.TryGetValue(baseType, out entitySubmitMethods)) { DomainOperationEntry submitMethod = null; if (entitySubmitMethods.TryGetValue(operation, out submitMethod)) { return submitMethod; } } } return null; }
/// <summary> /// Recursive helper for <see cref="IsOperationSupported"/>. This method checks support /// for the type directly, then checks composing parents as well. /// </summary> /// <param name="entityType">The entity Type to check.</param> /// <param name="operationType">The operation Type to check.</param> /// <param name="isParent">True if the check should use compositional parent rules.</param> /// <param name="visited">Visited map used during recursion.</param> /// <returns><c>True</c> if the operation is supported, <c>false</c> otherwise.</returns> private bool IsOperationSupportedInternal(Type entityType, DomainOperation operationType, bool isParent, HashSet<Type> visited) { if (this.GetSubmitMethod(entityType, operationType) != null) { // an explicit operation exists return true; } if (operationType == DomainOperation.Update || isParent) { // when checking operation support for a composed child, // if the parent supports Update, all operations are supported // for the child bool canUpdate = this.GetSubmitMethod(entityType, DomainOperation.Update) != null || this.GetCustomMethods(entityType).Any(); if (canUpdate) { return true; } } // Avoid infinite recursion in the case of composition cycles if (visited.Contains(entityType)) { return false; } visited.Add(entityType); // for compositional children, if the parent supports the operation (or supports // Update) the operation is supported. foreach (PropertyDescriptor parentAssociation in this.GetParentAssociations(entityType)) { if (this.IsOperationSupportedInternal(parentAssociation.ComponentType, operationType, true, visited)) { return true; } } return false; }
/// <summary> /// Determines whether the specified change operation is supported for the specified Type. /// If the Type is the child of one or more composition relationships, operation support /// takes parent support into account. /// </summary> /// <param name="entityType">The entity Type to check.</param> /// <param name="operationType">The operation Type to check. Must be one of the /// change operation types Insert, Update or Delete.</param> /// <returns><c>True</c> if the operation is supported, <c>false</c> otherwise.</returns> public bool IsOperationSupported(Type entityType, DomainOperation operationType) { if (entityType == null) { throw new ArgumentNullException("entityType"); } if (operationType != DomainOperation.Insert && operationType != DomainOperation.Update && operationType != DomainOperation.Delete) { throw new ArgumentOutOfRangeException("operationType"); } HashSet<Type> visited = new HashSet<Type>(); return this.IsOperationSupportedInternal(entityType, operationType, false, visited); }
/// <summary> /// Validates that every CUD method (aka domain operation) exposed on a derived /// entity is also exposed on that entity's root. /// </summary> /// <exception cref="InvalidOperationException">if any derived entity exposes a domain operation not also on the root.</exception> private void ValidateDerivedDomainOperations() { HashSet<Type> rootEntityTypes = new HashSet<Type>(this.RootEntityTypes); IEnumerable<Type> derivedEntityTypes = this.EntityTypes.Where(t => !rootEntityTypes.Contains(t)); DomainOperation[] allDomainOperations = new DomainOperation[] { DomainOperation.Insert, DomainOperation.Update, DomainOperation.Delete }; foreach (Type derivedType in derivedEntityTypes) { Type rootType = this.GetRootEntityType(derivedType); // Loop over Insert, Update, Delete for each entity foreach (DomainOperation domainOperation in allDomainOperations) { DomainOperationEntry derivedOperation = this.GetSubmitMethod(derivedType, domainOperation); if (derivedOperation != null) { DomainOperationEntry rootOperation = this.GetSubmitMethod(rootType, domainOperation); if (rootOperation == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.DomainOperation_Required_On_Root, derivedOperation.Name, derivedType.Name, rootType.Name)); } } } } }
private static bool IsValidMethodSignature(DomainServiceDescription description, DomainOperationEntry operationEntry, DomainOperation operation, out Exception error) { string methodName = operationEntry.Name; ReadOnlyCollection<DomainOperationParameter> parameters = operationEntry.Parameters; Type returnType = operationEntry.ReturnType; switch (operation) { case DomainOperation.Delete: case DomainOperation.Insert: case DomainOperation.Update: { // insert signature check: parameter length must be 1 if (parameters.Count != 1) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidInsertUpdateDeleteMethod_IncorrectParameterLength, methodName)); return false; } // parameter must be by-value if (!IsByVal(parameters[0])) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainOperationEntry_ParamMustBeByVal, methodName, parameters[0].Name)); return false; } if (!description._entityTypes.Contains(parameters[0].ParameterType)) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainMethod_ParamMustBeEntity, parameters[0].Name, methodName)); return false; } break; } case DomainOperation.Query: { // Ignore the optional "out count" parameter. IEnumerable<DomainOperationParameter> queryParameters = parameters; DomainOperationParameter lastParameter = queryParameters.LastOrDefault(); if (lastParameter != null && lastParameter.IsOut) { queryParameters = queryParameters.Take(queryParameters.Count() - 1).ToArray(); } foreach (DomainOperationParameter param in queryParameters) { if (!TypeUtility.IsPredefinedType(param.ParameterType)) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainOperationEntry_ParamMustBeSimple, methodName, param.Name)); return false; } } // Determine the entity Type and validate the return type // (must return IEnumerable<T> or a singleton) bool isSingleton = false; Type entityType = DomainServiceDescription.GetQueryEntityReturnType(operationEntry, out isSingleton, out error); if (error != null) { return false; } // validate the entity Type if (entityType == null || !description._entityTypes.Contains(entityType)) { string errorMessage; if (!IsValidEntityType(entityType, out errorMessage)) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.Invalid_Entity_Type, entityType.Name, errorMessage)); return false; } } // Only IEnumerable<T> returning query methods can be marked composable if (isSingleton && ((QueryAttribute)operationEntry.OperationAttribute).IsComposable) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.DomainServiceDescription_SingletonQueryMethodCannotCompose, methodName, returnType)); } break; } case DomainOperation.Custom: { // check that the method signature is conforming to our expectations (Entity, one or more of pre-defined types) if (parameters.Count == 0) { error = new InvalidOperationException(Resource.InvalidCustomMethod_MethodCannotBeParameterless); return false; } bool first = true; foreach (DomainOperationParameter param in parameters) { if (first) { // if first parameter, ensure that its type is one of the Entity types associated with CRUD. if (!description._entityTypes.Contains(param.ParameterType)) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainMethod_ParamMustBeEntity, param.Name, methodName)); return false; } first = false; } else if (!TypeUtility.IsPredefinedType(param.ParameterType) && !TypeUtility.IsSupportedComplexType(param.ParameterType)) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainOperationEntry_ParamMustBeSimple, methodName, param.Name)); return false; } } break; } case DomainOperation.Invoke: { foreach (DomainOperationParameter param in parameters) { // parameter Type must be one of the predefined types, a supported complex type, an entity or collection of entities if (!description._entityTypes.Contains(param.ParameterType) && !TypeUtility.IsPredefinedType(param.ParameterType) && !TypeUtility.IsSupportedComplexType(param.ParameterType)) { // see if the parameter type is a supported collection of entities Type elementType = TypeUtility.GetElementType(param.ParameterType); bool isEntityCollection = description._entityTypes.Contains(elementType) && TypeUtility.IsSupportedCollectionType(param.ParameterType); if (!isEntityCollection) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidInvokeOperation_ParameterType, methodName)); return false; } } } // return Type must be one of the predefined types, an entity or collection of entities if (returnType != typeof(void) && !description._entityTypes.Contains(returnType) && !TypeUtility.IsPredefinedType(returnType) && !TypeUtility.IsSupportedComplexType(returnType)) { // see if the return is a supported collection of entities Type elementType = TypeUtility.GetElementType(returnType); bool isEntityCollection = description._entityTypes.Contains(elementType) && TypeUtility.IsSupportedCollectionType(returnType); if (!isEntityCollection) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidInvokeOperation_ReturnType, methodName)); return false; } } break; } } if (operation != DomainOperation.Invoke && operation != DomainOperation.Query) { // return type should be void for other domain operations except invoke operations if (returnType != typeof(void) || operationEntry.IsTaskAsync) { error = new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidDomainOperationEntry_NonQueryMustReturnVoid, methodName)); return false; } } error = null; return true; }
/// <summary> /// Defines a proxy Submit operation. /// </summary> /// <param name="context">The generation context.</param> /// <param name="method">The contract method.</param> /// <param name="domainOperation">The type of <see cref="DomainOperation"/>.</param> private static void GenerateSubmitMethod(GenerationContext context, MethodInfo method, DomainOperation domainOperation) { /* Here's the C# equivalent of what we want to generate: * * public void $(MethodName)($(EntityParameter), (... [$(ParameterAttributes)] $(Parameters))) * { * DomainServiceProxyHelpers.Submit(this, entity, operationName, parameters, domainOperation) * } * */ ParameterInfo[] parameters = method.GetParameters(); Type[] parameterTypes = parameters.Select(p => p.IsOut || p.ParameterType.IsByRef ? p.ParameterType.GetElementType() : p.ParameterType).ToArray(); MethodBuilder queryMethod = context.TypeBuilder.DefineMethod( method.Name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, method.ReturnType, parameters.Select(pi => pi.ParameterType).ToArray()); InitializeMethod(queryMethod, method); ILGenerator methodBody = queryMethod.GetILGenerator(); // Emit: // // object[] parameterValues; LocalBuilder paramValuesLocal = methodBody.DeclareLocal(typeof(object[])); // Emit: // // // For all 'out' parameters // <parameter> = default(); for (int i = 0; i < parameters.Length; ++i) { ParameterInfo parameter = parameters[i]; if (parameter.IsOut && !parameter.IsIn) { methodBody.Emit(OpCodes.Ldarg_S, i + 1); // <parameter> methodBody.Emit(OpCodes.Initobj, parameterTypes[i]); // <parameter> = default(); } } // Do we need to pass parameters along? For a submit method, we can // assume the first parameter is the entity so we check to see if // additional parameters exist beyond that. if ((parameters.Length - 1) < 1) { // Emit: // // parameterValues = null; methodBody.Emit(OpCodes.Ldnull); methodBody.Emit(OpCodes.Stloc, paramValuesLocal); } else { // Emit: // // parameterValues = new object[ $(Parameters).Length ]; methodBody.Emit(OpCodes.Ldc_I4_S, parameters.Length - 1); methodBody.Emit(OpCodes.Newarr, typeof(object)); methodBody.Emit(OpCodes.Stloc, paramValuesLocal); // Emit: // // // Repeat for all parameter values... // parameterValues[n] = $(Parameters)[n]; for (int i = 1; i < parameters.Length; i++) { ParameterInfo param = parameters[i]; methodBody.Emit(OpCodes.Ldloc, paramValuesLocal); // parameterValues methodBody.Emit(OpCodes.Ldc_I4_S, i - 1); methodBody.Emit(OpCodes.Ldarg_S, i + 1); // parameterValues[n] // out/ref types? push on stack if (param.IsOut || param.ParameterType.IsByRef) { methodBody.Emit(OpCodes.Ldobj, parameterTypes[i]); } // value or generics? box it up if (parameterTypes[i].IsValueType || parameterTypes[i].IsGenericParameter) { methodBody.Emit(OpCodes.Box, parameterTypes[i]); } methodBody.Emit(OpCodes.Stelem_Ref); // parameterValues[n] = arg[n] } } // Emit: // // object[] objectParams = new object[8]; LocalBuilder objectParams = methodBody.DeclareLocal(typeof(object[])); methodBody.Emit(OpCodes.Ldc_I4_8); methodBody.Emit(OpCodes.Newarr, typeof(object)); methodBody.Emit(OpCodes.Stloc_S, objectParams); // Emit: // // objectParams[0] = this._domainServiceType; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 0); methodBody.Emit(OpCodes.Ldarg_0); methodBody.Emit(OpCodes.Ldfld, context.DomainServiceTypeField); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[1] = this._domainServiceContext; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 1); methodBody.Emit(OpCodes.Ldarg_0); methodBody.Emit(OpCodes.Ldfld, context.DomainServiceContextField); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[2] = this.DomainServiceInstances; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 2); methodBody.Emit(OpCodes.Ldarg_0); methodBody.Emit(OpCodes.Callvirt, context.DomainServiceInstancesGetter); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[3] = this.CurrentOriginalEntityMap; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 3); methodBody.Emit(OpCodes.Ldarg_0); methodBody.Emit(OpCodes.Callvirt, context.DomainServiceCurrentOriginalGetter); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[4] = entity; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 4); methodBody.Emit(OpCodes.Ldarg_1); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[5] = operationName; // if a custom operation methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 5); if (domainOperation == DomainOperation.Custom) { methodBody.Emit(OpCodes.Ldstr, method.Name); } else { methodBody.Emit(OpCodes.Ldnull); } methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[6] = parameters; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 6); methodBody.Emit(OpCodes.Ldloc_S, paramValuesLocal); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // objectParams[7] = (int)domainOperation; methodBody.Emit(OpCodes.Ldloc_S, objectParams); methodBody.Emit(OpCodes.Ldc_I4_S, 7); methodBody.Emit(OpCodes.Ldc_I4_S, (int)domainOperation); methodBody.Emit(OpCodes.Box, typeof(int)); methodBody.Emit(OpCodes.Stelem_Ref); // Emit: // // submitDelegate.DynamicInvoke(objectParams); // leaving result on stack EmitDelegateInvoke(methodBody, objectParams, context.SubmitDelegateField); methodBody.Emit(OpCodes.Pop); // ignore result, its a void returning method methodBody.Emit(OpCodes.Ret); }
private static void EnsureOperationSupported(OperationContext context, Type entityType, DomainOperation operationType) { if (!context.DomainServiceDescription.IsOperationSupported(entityType, operationType)) { throw new DomainServiceTestHostException(string.Format( CultureInfo.CurrentCulture, Resources.OperationNotSupported, operationType, entityType, context.DomainServiceDescription.DomainServiceType)); } }
public bool Setup(string functionsXMLFilename) { try { XElement x = XElement.Load(functionsXMLFilename, LoadOptions.None); XElement helpers = x.Element("helpers"); foreach (XElement h in helpers.Elements("code")) { XAttribute comment = h.Attribute("comment"); HelperCode helper = new HelperCode(h.Attribute("name").Value, h.Value, comment == null ? "" : comment.Value); Helpers.Add(helper.Name, helper); } XElement nodes = x.Element("nodes"); foreach (XElement n in nodes.Elements("node")) { string code = ""; string[] requires = null; XElement func = n.Element("function"); if (func != null) { code = func.Value; XAttribute requ = func.Attribute("requires"); if (requ != null) { requires = requ.Value.Split(",".ToCharArray()); } } XAttribute com = n.Attribute("comment"); string comment = ""; if (com != null) { comment = com.Value; } SceneNode node = null; switch (n.Attribute("type").Value) { case "dp": node = new DistancePrimitive(n.Attribute("name").Value, n.Attribute("caption").Value, n.Attribute("mask").Value, code, requires, comment); DistanceFieldTypes.Add(node as DistancePrimitive); break; case "distop": node = new DistanceOperation(n.Attribute("name").Value, n.Attribute("caption").Value, n.Attribute("mask").Value, code, requires, comment); DistanceOperations.Add(node as DistanceOperation); break; case "domop": node = new DomainOperation(n.Attribute("name").Value, n.Attribute("caption").Value, n.Attribute("mask").Value, code, requires, comment); DomainOperations.Add(node as DomainOperation); break; } if (node != null && n.Element("properties") != null) { foreach (XElement p in n.Element("properties").Elements("property")) { node.Properties.Add(createInputProperty(p.Attribute("type").Value, p.Attribute("name").Value, p.Attribute("default").Value)); } } } } catch (Exception e) { errors.Add("Failed to load/parse " + functionsXMLFilename + ":\n" + e.Message); return(false); } return(true); }
public static ChangeSetEntry GetChangeSetEntry(OperationContext context, object entity, object original, DomainOperation operationType) { Utility.EnsureOperationSupported(context, entity.GetType(), operationType); context.OperationName = context.DomainServiceDescription.GetSubmitMethod(entity.GetType(), operationType).Name; return(new ChangeSetEntry(Utility.DefaultChangeSetEntryId, entity, original, operationType)); }