/// <summary> /// Resets the given resource /// </summary> /// <param name="resource">The resource to reset</param> /// <returns>The reset resource</returns> public virtual object ResetResource(object resource) { ExceptionUtilities.CheckArgumentNotNull(resource, "resource"); var token = UpdatableToken.AssertIsToken(resource, "resource"); resource = token.Resource; // create a new token token = new UpdatableToken(resource); var instance = resource as ResourceInstance; ExceptionUtilities.CheckObjectNotNull(instance, "Resource was not a resource instance. Type was '{0}'", resource.GetType()); var resourceType = this.metadataHelper.ResourceTypes.Single(t => t.FullName == instance.ResourceTypeName); foreach (ResourceProperty p in resourceType.GetAllPropertiesLazily()) { var property = p; if ((property.Kind & (ResourcePropertyKind.Key | ResourcePropertyKind.ResourceReference | ResourcePropertyKind.ResourceSetReference)) == 0) { // TODO: initialize to defaults? this.pendingChanges.Add(() => instance.Remove(property.Name)); } } // remove all dynamic properties // the ToList is so we can modify the collection inside the loop foreach (string key in instance.Keys.Except(resourceType.GetAllPropertiesLazily().Select(p => p.Name)).ToList()) { var propertyName = key; this.pendingChanges.Add(() => instance.Remove(propertyName)); } this.InitializeCollectionProperties(resourceType, instance, token, p => p.Kind == ResourcePropertyKind.Collection); return token; }
/// <summary> /// Creates a resource in the given set with the given type /// </summary> /// <param name="containerName">The given set name</param> /// <param name="fullTypeName">The given type name</param> /// <returns>The resource created</returns> public virtual object CreateResource(string containerName, string fullTypeName) { ExceptionUtilities.CheckArgumentNotNull(fullTypeName, "fullTypeName"); ResourceType type = this.metadataHelper.ResourceTypes.SingleOrDefault(t => t.FullName == fullTypeName); ExceptionUtilities.CheckObjectNotNull(type, "Could not find type with name '{0}'", fullTypeName); ExceptionUtilities.ThrowDataServiceExceptionIfFalse(!type.IsAbstract, 400, "Cannot create resource because type '{0}' is abstract.", type.FullName); ResourceInstance instance; if (type.ResourceTypeKind == ResourceTypeKind.ComplexType) { ExceptionUtilities.Assert(containerName == null, "Container name must be null for complex types. Value was '{0}'", containerName); if (type.InstanceType == typeof(ResourceInstance)) { instance = new ResourceInstance(type); } else { ConstructorInfo constructor = type.InstanceType.GetConstructor(new[] { typeof(ResourceType) }); ExceptionUtilities.CheckObjectNotNull(constructor, "Could not find constructor for complex type '{0}'", type.InstanceType); instance = (ResourceInstance)constructor.Invoke(new object[] { type }); } } else { ExceptionUtilities.CheckArgumentNotNull(containerName, "containerName"); ResourceSet set = this.GetResourceSet(containerName); ExceptionUtilities.CheckObjectNotNull(set, "Could not find resource set with name '{0}'", containerName); ExceptionUtilities.Assert(set.ResourceType == type || this.GetDerivedTypesInternal(set.ResourceType).Contains(type), "An entity of type '{0}' cannot be added to set '{1}'", fullTypeName, containerName); if (type.InstanceType == typeof(ResourceInstance)) { instance = new ResourceInstance(type, set); } else { ConstructorInfo constructor = type.InstanceType.GetConstructor(new[] { typeof(ResourceType), typeof(ResourceSet) }); ExceptionUtilities.CheckObjectNotNull(constructor, "Could not find constructor for entity type '{0}'", type.InstanceType); instance = (ResourceInstance)constructor.Invoke(new object[] { type, set }); } // TODO: better message ExceptionUtilities.ThrowDataServiceExceptionIfFalse(!this.ResourceSetsStorage[set.Name].Any(e => this.AreKeysEqual(e, instance)), 500, "Duplicate key"); this.pendingChanges.Add(() => this.ResourceSetsStorage[set.Name].Add(instance)); } var token = new UpdatableToken(instance); // foreach property marked as being server generated foreach (ResourceProperty property in type.GetAllPropertiesLazily()) { string propertyName = property.Name; object generatedValue; if (this.TryGetStoreGeneratedValue(containerName, fullTypeName, propertyName, out generatedValue)) { token.PendingPropertyUpdates[propertyName] = generatedValue; this.pendingChanges.Add(() => instance[propertyName] = generatedValue); } } this.InitializeCollectionProperties(type, instance, token, p => p.Kind == ResourcePropertyKind.ResourceSetReference || p.Kind == ResourcePropertyKind.Collection); return token; }
/// <summary> /// Gets the value of the given property /// </summary> /// <param name="targetResource">The resource to get the property value from</param> /// <param name="propertyName">The property name</param> /// <returns>The value of the property</returns> public virtual object GetValue(object targetResource, string propertyName) { ExceptionUtilities.CheckArgumentNotNull(targetResource, "targetResource"); ExceptionUtilities.CheckArgumentNotNull(propertyName, "propertyName"); // resolve the token, and return a pending value if there is one // NOTE: this code is specifically to handle cases of mapped complex-type values, because the product does not cache the // value returned by CreateResource so we need to take into account any pending updates, or we risk returning stale data var token = UpdatableToken.AssertIsToken(targetResource, "targetResource"); if (token.PendingPropertyUpdates.ContainsKey(propertyName)) { return token.PendingPropertyUpdates[propertyName]; } targetResource = token.Resource; var instance = targetResource as ResourceInstance; ExceptionUtilities.CheckObjectNotNull(instance, "Target resource was not a resource instance. Type was '{0}'", targetResource.GetType()); var resourceType = this.metadataHelper.ResourceTypes.Single(t => t.FullName == instance.ResourceTypeName); var property = resourceType.GetAllPropertiesLazily().SingleOrDefault(p => p.Name == propertyName); if (property != null) { ExceptionUtilities.Assert(!property.Kind.HasFlag(ResourcePropertyKind.Stream), "GetValue called on stream property '{0}'", property.Name); } object value = null; // Check for strongly typed properties var type = targetResource.GetType(); PropertyInfo propertyInfo = type.GetProperty(propertyName); if (propertyInfo != null) { value = propertyInfo.GetValue(targetResource, null); } else { object propertyValue; if (instance.TryGetValue(propertyName, out propertyValue)) { value = propertyValue; } } var valueInstance = value as ResourceInstance; if (valueInstance != null) { ExceptionUtilities.Assert(!valueInstance.IsEntityType, "GetValue should never be called for reference properties. Type was '{0}', property was '{1}'", resourceType.FullName, propertyName); value = new UpdatableToken(valueInstance); } return value; }
private void InitializeCollectionProperties(ResourceType type, ResourceInstance instance, UpdatableToken token, Func<ResourceProperty, bool> filter) { foreach (ResourceProperty property in type.GetAllPropertiesLazily().Where(filter)) { string propertyName = property.Name; Type collectionType = this.GetCollectionPropertyType(type.FullName, propertyName); if (collectionType != null) { var newCollection = Activator.CreateInstance(collectionType); token.PendingPropertyUpdates[propertyName] = newCollection; this.pendingChanges.Add(() => instance[propertyName] = newCollection); } } }