private static void VerifyIsVersion4(Type type) { ClientEdmModel model = new ClientEdmModel(ODataProtocolVersion.V4); IEdmType edmType = model.GetOrCreateEdmType(type); var typeAnnotation = model.GetClientTypeAnnotation(edmType); Assert.AreEqual(typeAnnotation.GetMetadataVersion(), new Version(4, 0), "Type with geography point must have 4.0 version"); }
/// <summary> /// Creates an instance of TypeResolver class. /// </summary> /// <param name="model">The client model.</param> /// <param name="resolveTypeFromName">The callback to resolve client CLR types.</param> /// <param name="resolveNameFromType">The callback for resolving server type names.</param> /// <param name="serviceModel">The service model.</param> internal TypeResolver(ClientEdmModel model, Func<string, Type> resolveTypeFromName, Func<Type, string> resolveNameFromType, IEdmModel serviceModel) { Debug.Assert(model != null, "model != null"); Debug.Assert(resolveTypeFromName != null, "resolveTypeFromName != null"); Debug.Assert(resolveNameFromType != null, "resolveNameFromType != null"); this.resolveTypeFromName = resolveTypeFromName; this.resolveNameFromType = resolveNameFromType; this.serviceModel = serviceModel; this.clientEdmModel = model; if (serviceModel != null && clientEdmModel != null) { if (clientEdmModel.EdmStructuredSchemaElements == null) { clientEdmModel.EdmStructuredSchemaElements = serviceModel.SchemaElements.Where(se => se is IEdmStructuredType).ToList(); } else { foreach (var element in serviceModel.SchemaElements.Where(se => se is IEdmStructuredType)) { if (!clientEdmModel.EdmStructuredSchemaElements.Contains(element)) { clientEdmModel.EdmStructuredSchemaElements.Add(element); } } } } }
public void ShortIntegrationTestToValidateEntryShouldBeRead() { var odataEntry = new ODataEntry() { Id = new Uri("http://services.odata.org/OData/OData.svc/Customers(0)") }; odataEntry.Properties = new ODataProperty[] { new ODataProperty() { Name = "ID", Value = 0 }, new ODataProperty() { Name = "Description", Value = "Simple Stuff" } }; var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var context = new DataServiceContext(); MaterializerEntry.CreateEntry(odataEntry, ODataFormat.Atom, true, clientEdmModel); var materializerContext = new TestMaterializerContext() {Model = clientEdmModel, Context = context}; var adapter = new EntityTrackingAdapter(new TestEntityTracker(), MergeOption.OverwriteChanges, clientEdmModel, context); QueryComponents components = new QueryComponents(new Uri("http://foo.com/Service"), new Version(4, 0), typeof(Customer), null, new Dictionary<Expression, Expression>()); var entriesMaterializer = new ODataEntriesEntityMaterializer(new ODataEntry[] { odataEntry }, materializerContext, adapter, components, typeof(Customer), null, ODataFormat.Atom); var customersRead = new List<Customer>(); // This line will call ODataEntityMaterializer.ReadImplementation() which will reconstruct the entity, and will get non-public setter called. while (entriesMaterializer.Read()) { customersRead.Add(entriesMaterializer.CurrentValue as Customer); } customersRead.Should().HaveCount(1); customersRead[0].ID.Should().Be(0); customersRead[0].Description.Should().Be("Simple Stuff"); }
/// <summary> /// Instantiates a new Serializer class and calls WriteEntry method on it. /// </summary> /// <param name="dataServiceContext"></param> /// <returns></returns> private static Person SetupSerializerAndCallWriteEntry(DataServiceContext dataServiceContext) { Person person = new Person(); Address address = new Address(); Car car1 = new Car(); person.Cars.Add(car1); person.HomeAddress = address; dataServiceContext.AttachTo("Cars", car1); dataServiceContext.AttachTo("Addresses", address); var requestInfo = new RequestInfo(dataServiceContext); var serializer = new Serializer(requestInfo); var headers = new HeaderCollection(); var clientModel = new ClientEdmModel(ODataProtocolVersion.V4); var entityDescriptor = new EntityDescriptor(clientModel); entityDescriptor.State = EntityStates.Added; entityDescriptor.Entity = person; var requestMessageArgs = new BuildingRequestEventArgs("POST", new Uri("http://www.foo.com/Northwind"), headers, entityDescriptor, HttpStack.Auto); var linkDescriptors = new LinkDescriptor[] { new LinkDescriptor(person, "Cars", car1, clientModel), new LinkDescriptor(person, "HomeAddress", address, clientModel) }; var odataRequestMessageWrapper = ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, requestInfo); serializer.WriteEntry(entityDescriptor, linkDescriptors, odataRequestMessageWrapper); return person; }
public void Initialize() { this.clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); this.clientEdmModel.GetOrCreateEdmType(typeof(TestCustomer)); this.clientEdmModel.GetOrCreateEdmType(typeof(TestOrder)); this.materializerContext = new TestMaterializerContext() { Model = this.clientEdmModel }; this.ordersProperty = this.clientEdmModel.GetClientTypeAnnotation(typeof(TestCustomer)).GetProperty("Orders", false); }
/// <summary> /// Initializes a new instance of the <see cref="EntityTrackingAdapter" /> class. /// </summary> /// <param name="entityTracker">The entity tracker.</param> /// <param name="mergeOption">The merge option.</param> /// <param name="model">The model.</param> /// <param name="context">The context.</param> internal EntityTrackingAdapter(EntityTrackerBase entityTracker, MergeOption mergeOption, ClientEdmModel model, DataServiceContext context) { this.MaterializationLog = new AtomMaterializerLog(mergeOption, model, entityTracker); this.MergeOption = mergeOption; this.EntityTracker = entityTracker; this.Model = model; this.Context = context; }
/// <summary> /// Initializes a new instance of the <see cref="FeedAndEntryMaterializerAdapter"/> class. Used for tests so no ODataMessageReader is required /// </summary> /// <param name="odataFormat">The format of the reader.</param> /// <param name="reader">The reader.</param> /// <param name="model">The model.</param> /// <param name="mergeOption">The mergeOption.</param> internal FeedAndEntryMaterializerAdapter(ODataFormat odataFormat, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption) { this.readODataFormat = odataFormat; this.clientEdmModel = model; this.mergeOption = mergeOption; this.reader = reader; this.currentEntry = null; this.currentFeed = null; this.feedEntries = null; }
/// <summary> /// Initializes a new <see cref="AtomMaterializerLog"/> instance. /// </summary> /// <param name="mergeOption">The merge option for the log.</param> /// <param name="model">The model for the log.</param> /// <param name="entityTracker">The entity tracker for the log.</param> /// <remarks> /// Note that the merge option can't be changed. /// </remarks> internal AtomMaterializerLog(MergeOption mergeOption, ClientEdmModel model, EntityTrackerBase entityTracker) { Debug.Assert(model != null, "model != null"); Debug.Assert(entityTracker != null, "entityTracker != null"); this.appendOnlyEntries = new Dictionary<Uri, ODataEntry>(EqualityComparer<Uri>.Default); this.mergeOption = mergeOption; this.clientEdmModel = model; this.entityTracker = entityTracker; this.identityStack = new Dictionary<Uri, ODataEntry>(EqualityComparer<Uri>.Default); this.links = new List<LinkDescriptor>(); }
public void Init() { this._address = new Address { Street = "123 fake st" }; this._entity = new Customer { Id = 1, Address = this._address, Emails = new List<string> { "*****@*****.**"} }; var model = new ClientEdmModel(ODataProtocolVersion.V4); var entityType = model.GetOrCreateEdmType(typeof(Customer)); var complexType = model.GetOrCreateEdmType(typeof(Address)); this._complexValue = new ClientEdmStructuredValue(this._address, model, model.GetClientTypeAnnotation(complexType)); this._entityValue = new ClientEdmStructuredValue(this._entity, model, model.GetClientTypeAnnotation(entityType)); }
public void ValidateShortIntegrationFeedReading() { var initialFeed = new ODataFeed() {Id = new Uri("http://services.odata.org/OData/OData.svc/Products")}; var productItem = new ODataEntry() {Id = new Uri("http://services.odata.org/OData/OData.svc/Products(0)")}; productItem.Properties = new ODataProperty[] {new ODataProperty() {Name = "Id", Value = 0}}; var categoryNavigationLink = new ODataNavigationLink() {Name = "Category"}; var categoryItem = new ODataEntry() { Id = new Uri("http://services.odata.org/OData/OData.svc/Categories(0)") }; categoryItem.Properties = new ODataProperty[] { new ODataProperty() { Name = "Id", Value = 0 } }; var productsNavigationLink = new ODataNavigationLink() { Name = "Products" }; var supplierNavigationLink = new ODataNavigationLink() { Name = "Supplier" }; var testODataReader = new TestODataReader() { new TestODataReaderItem(ODataReaderState.FeedStart, initialFeed), new TestODataReaderItem(ODataReaderState.EntryStart, productItem), new TestODataReaderItem(ODataReaderState.NavigationLinkStart, categoryNavigationLink), new TestODataReaderItem(ODataReaderState.EntryStart, categoryItem), new TestODataReaderItem(ODataReaderState.NavigationLinkStart, productsNavigationLink), new TestODataReaderItem(ODataReaderState.NavigationLinkEnd, productsNavigationLink), new TestODataReaderItem(ODataReaderState.EntryEnd, categoryItem), new TestODataReaderItem(ODataReaderState.NavigationLinkEnd, categoryNavigationLink), new TestODataReaderItem(ODataReaderState.NavigationLinkStart, supplierNavigationLink), new TestODataReaderItem(ODataReaderState.NavigationLinkEnd, supplierNavigationLink), new TestODataReaderItem(ODataReaderState.EntryEnd, productItem), new TestODataReaderItem(ODataReaderState.FeedEnd, initialFeed), }; ClientEdmModel clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); var responsePipeline = new DataServiceClientResponsePipelineConfiguration(new DataServiceContext()); var odataReaderWrapper = ODataReaderWrapper.CreateForTest(testODataReader, responsePipeline); FeedAndEntryMaterializerAdapter reader = new FeedAndEntryMaterializerAdapter(ODataFormat.Atom, odataReaderWrapper, clientEdmModel, MergeOption.OverwriteChanges); int readCounter = 0; while (reader.Read()) { readCounter++; } readCounter.Should().Be(2); }
/// <summary> /// Creates an instance of TypeResolver class. /// </summary> /// <param name="model">The client model.</param> /// <param name="resolveTypeFromName">The callback to resolve client CLR types.</param> /// <param name="resolveNameFromType">The callback for resolving server type names.</param> /// <param name="serviceModel">The service model.</param> internal TypeResolver(ClientEdmModel model, Func <string, Type> resolveTypeFromName, Func <Type, string> resolveNameFromType, IEdmModel serviceModel) { Debug.Assert(model != null, "model != null"); Debug.Assert(resolveTypeFromName != null, "resolveTypeFromName != null"); Debug.Assert(resolveNameFromType != null, "resolveNameFromType != null"); this.resolveTypeFromName = resolveTypeFromName; this.resolveNameFromType = resolveNameFromType; this.serviceModel = serviceModel; this.clientEdmModel = model; if (serviceModel != null && clientEdmModel != null) { foreach (var element in serviceModel.SchemaElements.Where(se => se is IEdmStructuredType)) { clientEdmModel.EdmStructuredSchemaElements.TryAdd(element.Name, element); } } }
/// <summary> /// Write the entry element. /// </summary> /// <param name="entityDescriptor">The entity.</param> /// <param name="relatedLinks">Collection of links related to the entity.</param> /// <param name="requestMessage">The OData request message.</param> internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage) { ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation entityType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType())); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/)) { ODataWriterWrapper entryWriter = ODataWriterWrapper.CreateForEntry(messageWriter, this.requestInfo.Configurations.RequestPipeline); // Get the server type name using the type resolver or from the entity descriptor string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor); var entry = CreateODataEntry(entityDescriptor, serverTypeName, entityType, this.requestInfo.Format); if (serverTypeName == null) { serverTypeName = this.requestInfo.InferServerTypeNameFromServerModel(entityDescriptor); } IEnumerable <ClientPropertyAnnotation> properties; if ((!Util.IsFlagSet(this.options, SaveChangesOptions.ReplaceOnUpdate) && entityDescriptor.State == EntityStates.Modified && entityDescriptor.PropertiesToSerialize.Any()) || (Util.IsFlagSet(this.options, SaveChangesOptions.PostOnlySetProperties) && entityDescriptor.State == EntityStates.Added)) { properties = entityType.PropertiesToSerialize().Where(prop => entityDescriptor.PropertiesToSerialize.Contains(prop.PropertyName)); } else { properties = entityType.PropertiesToSerialize(); } entry.Properties = this.propertyConverter.PopulateProperties(entityDescriptor.Entity, serverTypeName, properties); entryWriter.WriteStart(entry, entityDescriptor.Entity); if (EntityStates.Added == entityDescriptor.State) { this.WriteNavigationLink(entityDescriptor, relatedLinks, entryWriter); } entryWriter.WriteEnd(entry, entityDescriptor.Entity); } }
/// <summary> /// Get or create a client EDM type instance. /// </summary> /// <param name="type">type to wrap</param> /// <returns>client type</returns> internal IEdmType GetOrCreateEdmType(Type type) { Debug.Assert(type != null, "type != null"); EdmTypeCacheValue cachedEdmType; lock (this.clrToEdmTypeCache) { this.clrToEdmTypeCache.TryGetValue(type, out cachedEdmType); } if (cachedEdmType == null) { if (PrimitiveType.IsKnownNullableType(type)) { cachedEdmType = this.GetOrCreateEdmTypeInternal(null /*baseType*/, type, ClientTypeUtil.EmptyPropertyInfoArray, false /*isEntity*/, false /*hasProperties*/); } else { PropertyInfo[] keyProperties; bool hasProperties; Type[] hierarchy = ClientEdmModel.GetTypeHierarchy(type, out keyProperties, out hasProperties); Debug.Assert(keyProperties == null || keyProperties.Length == 0 || keyProperties.All(p => p.DeclaringType == keyProperties[0].DeclaringType), "All key properties must be declared on the same type."); bool isEntity = keyProperties != null; keyProperties = keyProperties ?? ClientTypeUtil.EmptyPropertyInfoArray; foreach (Type t in hierarchy) { // Pass in the full list of key properties for the most base type to be added there. We only allow key properties to be // declared on the same type. IEdmStructuredType edmBaseType = cachedEdmType == null ? null : cachedEdmType.EdmType as IEdmStructuredType; cachedEdmType = this.GetOrCreateEdmTypeInternal(edmBaseType, t, keyProperties, isEntity, t == type ? hasProperties : (bool?)null); // Pass in an empty PropertyInfo array on subsequent derived types. keyProperties = ClientTypeUtil.EmptyPropertyInfoArray; } } } Debug.Assert(cachedEdmType != null, "cachedEdmType != null"); this.ValidateComplexTypeHasProperties(type, cachedEdmType); return(cachedEdmType.EdmType); }
/// <summary> /// Checks if the provided type is a collection type (i.e. it implements ICollection and the collection item type is not an entity). /// </summary> /// <param name="type">Type being checked.</param> /// <param name="model">The client model.</param> /// <returns>True if the CLR type is a collection compatible type. False otherwise.</returns> internal static bool IsCLRTypeCollection(Type type, ClientEdmModel model) { // char[] and byte[] implements ICollection<> but we should not threat them as collections since they are primitive types for us. if (!PrimitiveType.IsKnownNullableType(type)) { Type collectionType = ClientTypeUtil.GetImplementationType(type, typeof(ICollection <>)); if (collectionType != null) { // collectionType is ICollection so we know that the first generic parameter // is the collection item type if (!ClientTypeUtil.TypeIsEntity(collectionType.GetGenericArguments()[0], model)) { return(true); } } } return(false); }
public void Init() { this.clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); this.entityDescriptor = new EntityDescriptor(this.clientEdmModel) { Entity = new Customer() }; var serverType = new EdmEntityType("FQ.NS", "MyServerType"); serverType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo { Name = "Navigation", Target = serverType, TargetMultiplicity = EdmMultiplicity.ZeroOrOne }); this.serverTypeName = ((IEdmSchemaElement)serverType).FullName(); var serverContainer = new EdmEntityContainer("FQ.NS", "MyContainer"); this.serverEntitySet = serverContainer.AddEntitySet("MyEntitySet", serverType); var serverModel = new EdmModel(); serverModel.AddElement(serverType); serverModel.AddElement(serverContainer); this.ctx = new DataServiceContext(new Uri("http://temp.org/"), ODataProtocolVersion.V4, this.clientEdmModel); this.ctx.Format.UseJson(serverModel); this.testSubject = new RequestInfo(ctx); }
/// <summary> /// Validates the specified <paramref name="property"/> matches /// the parsed <paramref name="link"/>. /// </summary> /// <param name="property">Property as understood by the type system.</param> /// <param name="link">Property as parsed.</param> /// <param name="model">Client Model.</param> /// <param name="performEntityCheck">whether to do the entity check or not.</param> /// <returns>The type</returns> internal static Type ValidatePropertyMatch(ClientPropertyAnnotation property, ODataNavigationLink link, ClientEdmModel model, bool performEntityCheck) { Debug.Assert(property != null, "property != null"); Debug.Assert(link != null, "link != null"); Type propertyType = null; if (link.IsCollection.HasValue) { if (link.IsCollection.Value) { // We need to fail if the payload states that the property is a navigation collection property // and in the client, the property is not a collection property. if (!property.IsEntityCollection) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName)); } propertyType = property.EntityCollectionItemType; } else { if (property.IsEntityCollection) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName)); } propertyType = property.PropertyType; } } // If the server type and the client type does not match, we need to throw. // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa if (propertyType != null && performEntityCheck) { if (!ClientTypeUtil.TypeIsEntity(propertyType, model)) { throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(propertyType.ToString())); } } return propertyType; }
/// <summary> /// Converts CLR value into ODataEnumValue. /// </summary> /// <param name="enumClrType">The CLR type.</param> /// <param name="value">The Enum value.</param> /// <param name="isCollectionItem">The bool isCollectionItem.</param> /// <returns>An ODataEnumValue instance.</returns> internal ODataEnumValue CreateODataEnumValue(Type enumClrType, object value, bool isCollectionItem) { Debug.Assert(enumClrType != null && enumClrType.IsEnum(), "enumClrType != null && enumClrType.IsEnum"); Debug.Assert(value != null || !isCollectionItem, "Collection items must not be null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation enumTypeAnnotation = model.GetClientTypeAnnotation(enumClrType); Debug.Assert(enumTypeAnnotation != null, "enumTypeAnnotation != null"); Debug.Assert(!enumTypeAnnotation.IsEntityType, "Unexpected entity"); // Handle null value by putting m:null="true" if (value == null) { Debug.Assert(!isCollectionItem, "Null collection items are not supported. Should have already been checked."); return(null); } return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(value.ToString(), enumClrType), enumTypeAnnotation.ElementTypeName)); }
/// <summary> /// Initializes a new instance of the <see cref="ClientEdmStructuredValue"/> class. /// </summary> /// <param name="structuredValue">The structured value.</param> /// <param name="model">The model.</param> /// <param name="clientTypeAnnotation">The client type annotation.</param> public ClientEdmStructuredValue(object structuredValue, ClientEdmModel model, ClientTypeAnnotation clientTypeAnnotation) { Debug.Assert(structuredValue != null, "entity != null"); Debug.Assert(model != null, "model != null"); Debug.Assert(clientTypeAnnotation != null, "clientTypeAnnotation != null"); if (clientTypeAnnotation.EdmType.TypeKind == EdmTypeKind.Complex) { // TODO: nullable? this.Type = new EdmComplexTypeReference((IEdmComplexType)clientTypeAnnotation.EdmType, true); } else { Debug.Assert(clientTypeAnnotation.EdmType.TypeKind == EdmTypeKind.Entity, "Only complex and entity values supported"); // TODO: nullable? this.Type = new EdmEntityTypeReference((IEdmEntityType)clientTypeAnnotation.EdmType, true); } this.structuredValue = structuredValue; this.typeAnnotation = clientTypeAnnotation; this.model = model; }
/// <summary> /// Initializes a new instance of the <see cref="ClientEdmStructuredValue"/> class. /// </summary> /// <param name="structuredValue">The structured value.</param> /// <param name="model">The model.</param> /// <param name="clientTypeAnnotation">The client type annotation.</param> public ClientEdmStructuredValue(object structuredValue, ClientEdmModel model, ClientTypeAnnotation clientTypeAnnotation) { Debug.Assert(structuredValue != null, "entity != null"); Debug.Assert(model != null, "model != null"); Debug.Assert(clientTypeAnnotation != null, "clientTypeAnnotation != null"); if (clientTypeAnnotation.EdmType.TypeKind == EdmTypeKind.Complex) { // TODO: nullable? this.Type = new EdmComplexTypeReference((IEdmComplexType)clientTypeAnnotation.EdmType, true); } else { Debug.Assert(clientTypeAnnotation.EdmType.TypeKind == EdmTypeKind.Entity, "Only complex and entity values supported"); // TODO: nullable? this.Type = new EdmEntityTypeReference((IEdmEntityType)clientTypeAnnotation.EdmType, true); } this.structuredValue = structuredValue; this.typeAnnotation = clientTypeAnnotation; this.model = model; }
/// <summary> /// Get the entity set name for the target entity object. /// </summary> /// <param name="target">An entity object.</param> /// <param name="targetEntitySet">The 'currently known' entity set name for the target object.</param> /// <param name="model">The client model.</param> /// <returns>The entity set name for the target object.</returns> /// <remarks> /// Allow user code to provide the entity set name. If user code does not provide the entity set name, then /// this method will get the entity set name from the value of the EntitySetAttribute. /// The 'currently known' entity set name for top level collections can be provided through OEC constructor /// </remarks> internal static string GetEntitySet( object target, string targetEntitySet, ClientEdmModel model) { Debug.Assert(target != null, "Argument 'target' cannot be null."); Debug.Assert(BindingEntityInfo.IsEntityType(target.GetType(), model), "Argument 'target' must be an entity type."); // Here's the rules in order of priority for resolving entity set name // 1. EntitySet name passed in the constructor or extension methods of DataServiceCollection // 2. EntitySet name specified in the EntitySet attribute by the code gen. {Remember this attribute is // not generated in case of MEST) if (!String.IsNullOrEmpty(targetEntitySet)) { return(targetEntitySet); } else { // If there is not a 'currently known' entity set name to validate against, then there must be // EntitySet attribute on the entity type return(BindingEntityInfo.GetEntitySetAttribute(target.GetType(), model)); } }
/// <summary> /// Creates a new instance of MaterializerEntry. /// </summary> /// <param name="entry">The entry.</param> /// <param name="format">The format the entry was read in.</param> /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param> /// <param name="model">The client model.</param> private MaterializerEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model) { Debug.Assert(entry != null, "entry != null"); this.entry = entry; this.Format = format; this.entityDescriptor = new EntityDescriptor(model); #pragma warning disable 618 this.isAtomOrTracking = isTracking || this.Format == ODataFormat.Atom; #pragma warning restore 618 string serverTypeName = this.Entry.TypeName; SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation<SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { // If the annotation has a value use it. Otherwise, in JSON-Light, the types can be inferred from the // context URI even if they are not present on the wire, so just use the type name from the entry. if (serializationTypeNameAnnotation.TypeName != null || this.Format != ODataFormat.Json) { serverTypeName = serializationTypeNameAnnotation.TypeName; } } this.entityDescriptor.ServerTypeName = serverTypeName; }
/// <summary> /// constructor /// </summary> /// <param name="edmProperty">Back reference to the EdmProperty this annotation is part of.</param> /// <param name="propertyInfo">propertyInfo instance.</param> /// <param name="model">The client model.</param> internal ClientPropertyAnnotation(IEdmProperty edmProperty, PropertyInfo propertyInfo, ClientEdmModel model) { Debug.Assert(edmProperty != null, "edmProperty != null"); Debug.Assert(propertyInfo != null, "null propertyInfo"); // Property should always have DeclaringType Debug.Assert(propertyInfo.DeclaringType != null, "Property without a declaring type"); this.EdmProperty = edmProperty; this.PropertyName = ClientTypeUtil.GetServerDefinedName(propertyInfo); this.PropertyInfo = propertyInfo; this.NullablePropertyType = propertyInfo.PropertyType; this.PropertyType = Nullable.GetUnderlyingType(this.NullablePropertyType) ?? this.NullablePropertyType; this.DeclaringClrType = propertyInfo.DeclaringType; MethodInfo propertyGetMethod = propertyInfo.GetGetMethod(); // Add the parameter to make set method is returned even it is not public. Portable lib does not support this. #if PORTABLELIB MethodInfo propertySetMethod = propertyInfo.GetSetMethod(); #else MethodInfo propertySetMethod = propertyInfo.GetSetMethod(true); #endif ParameterExpression instance = Expression.Parameter(typeof(Object), "instance"); ParameterExpression value = Expression.Parameter(typeof(Object), "value"); // instance => (Object)(((T)instance).get_PropertyName()); <-- we need to box the value back to object to return this.propertyGetter = propertyGetMethod == null ? null : (Func<object, object>)Expression.Lambda( Expression.Convert( Expression.Call( Expression.Convert(instance, this.DeclaringClrType), propertyGetMethod), typeof(Object)), instance).Compile(); // (instance, value) => ((T)instance).set_PropertyName((T1)value); this.propertySetter = propertySetMethod == null ? null : (Action<object, object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.DeclaringClrType), propertySetMethod, Expression.Convert(value, this.NullablePropertyType)), instance, value).Compile(); this.Model = model; this.IsKnownType = PrimitiveType.IsKnownType(this.PropertyType); // non primitive types: dictionary/collections if (!this.IsKnownType) { var setMethodInfo = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(IDictionary<,>), "set_Item", out this.DictionaryValueType); if (setMethodInfo != null) { ParameterExpression propertyNameParam = Expression.Parameter(typeof(String), "propertyName"); // (instance, propertyName, value) => ((IDictionary<string, DictionaryValueType>)instance)[propertyName] = (DictionaryValueType)value; this.dictionarySetter = (Action<Object, String, Object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, typeof(IDictionary<,>).MakeGenericType(typeof(String), this.DictionaryValueType)), setMethodInfo, propertyNameParam, Expression.Convert(value, this.DictionaryValueType)), instance, propertyNameParam, value).Compile(); } else { var containsMethod = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(ICollection<>), "Contains", out this.collectionGenericType); var addMethod = ClientTypeUtil.GetAddToCollectionMethod(this.PropertyType, out this.collectionGenericType); var removeMethod = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(ICollection<>), "Remove", out this.collectionGenericType); var clearMethod = ClientTypeUtil.GetMethodForGenericType(this.PropertyType, typeof(ICollection<>), "Clear", out this.collectionGenericType); // (instance, value) => ((PropertyType)instance).Contains((CollectionType)value); returns boolean this.collectionContains = containsMethod == null ? null : (Func<Object, Object, Boolean>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), containsMethod, Expression.Convert(value, this.collectionGenericType)), instance, value).Compile(); // (instance, value) => ((PropertyType)instance).Add((CollectionType)value); this.collectionAdd = addMethod == null ? null : (Action<Object, Object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), addMethod, Expression.Convert(value, this.collectionGenericType)), instance, value).Compile(); // (instance, value) => ((PropertyType)instance).Remove((CollectionType)value); returns boolean this.collectionRemove = removeMethod == null ? null : (Func<Object, Object, Boolean>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), removeMethod, Expression.Convert(value, this.collectionGenericType)), instance, value).Compile(); // (instance) => ((PropertyType)instance).Clear(); this.collectionClear = clearMethod == null ? null : (Action<Object>)Expression.Lambda( Expression.Call( Expression.Convert(instance, this.PropertyType), clearMethod), instance).Compile(); } } Debug.Assert(this.collectionGenericType == null || this.DictionaryValueType == null, "collectionGenericType and DictionaryItemType mutually exclusive. (They both can be null though)."); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">the client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> internal static IList <BindingPropertyInfo> GetObservableProperties(Type entityType, ClientEdmModel model) { return(GetBindingEntityInfoFor(entityType, model).ObservableProperties); }
/// <summary> /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter: /// 1. calls ODataPropertyConverter to convert CLR object into ODataValue/primitive values. /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values. /// </summary> /// <param name="operationParameters">The list of operation parameters to write.</param> /// <param name="requestMessage">The OData request message used to write the operation parameters.</param> internal void WriteBodyOperationParameters(List <BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage) { Debug.Assert(requestMessage != null, "requestMessage != null"); Debug.Assert(operationParameters != null, "operationParameters != null"); Debug.Assert(operationParameters.Any(), "operationParameters.Any()"); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/)) { ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter((IEdmOperation)null); parameterWriter.WriteStart(); foreach (OperationParameter operationParameter in operationParameters) { if (operationParameter.Value == null) { parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value); } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Collection: { // TODO: just call ODataPropertyConverter.CreateODataCollection() IEnumerator enumerator = ((ICollection)operationParameter.Value).GetEnumerator(); ODataCollectionWriter collectionWriter = parameterWriter.CreateCollectionWriter(operationParameter.Name); ODataCollectionStart odataCollectionStart = new ODataCollectionStart(); collectionWriter.WriteStart(odataCollectionStart); while (enumerator.MoveNext()) { Object collectionItem = enumerator.Current; if (collectionItem == null) { throw new NotSupportedException(Strings.Serializer_NullCollectionParamterItemValue(operationParameter.Name)); } IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType()); Debug.Assert(edmItemType != null, "edmItemType != null"); switch (edmItemType.TypeKind) { case EdmTypeKind.Complex: { Debug.Assert(model.GetClientTypeAnnotation(edmItemType).ElementType != null, "edmItemType.GetClientTypeAnnotation().ElementType != null"); ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue( model.GetClientTypeAnnotation(edmItemType).ElementType, collectionItem, null /*propertyName*/, false /*isCollectionItem*/, null /*visitedComplexTypeObjects*/); Debug.Assert(complexValue != null, "complexValue != null"); collectionWriter.WriteItem(complexValue); break; } case EdmTypeKind.Primitive: { object primitiveItemValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(collectionItem, collectionItem.GetType()); collectionWriter.WriteItem(primitiveItemValue); break; } case EdmTypeKind.Enum: { ODataEnumValue enumTmp = this.propertyConverter.CreateODataEnumValue( model.GetClientTypeAnnotation(edmItemType).ElementType, collectionItem, false); collectionWriter.WriteItem(enumTmp); break; } default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind)); } } collectionWriter.WriteEnd(); collectionWriter.Flush(); break; } case EdmTypeKind.Complex: { Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null"); ODataComplexValue complexValue = this.propertyConverter.CreateODataComplexValue( model.GetClientTypeAnnotation(edmType).ElementType, operationParameter.Value, null /*propertyName*/, false /*isCollectionItemType*/, null /*visitedComplexTypeObjects*/); Debug.Assert(complexValue != null, "complexValue != null"); parameterWriter.WriteValue(operationParameter.Name, complexValue); break; } case EdmTypeKind.Primitive: object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType()); parameterWriter.WriteValue(operationParameter.Name, primitiveValue); break; case EdmTypeKind.Enum: ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue( model.GetClientTypeAnnotation(edmType).ElementType, operationParameter.Value, false); parameterWriter.WriteValue(operationParameter.Name, tmp); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind)); } } // else } // foreach parameterWriter.WriteEnd(); parameterWriter.Flush(); } }
/// <summary> /// ConstantExpression visit method /// </summary> /// <param name="c">The ConstantExpression expression to visit</param> /// <returns>The visited ConstantExpression expression </returns> internal override Expression VisitConstant(ConstantExpression c) { if (c.Value == null) { this.builder.Append(UriHelper.NULL); return(c); } // DEVNOTE: // Rather than forcing every other codepath to have the 'Try...' pattern for formatting, // we catch the InvalidOperationException here to change the exception type. // This is exceedingly rare, and not a scenario where performance is meaningful, so the // reduced complexity in all other call sites is worth the extra logic here. string result; BinaryExpression b = this.parent as BinaryExpression; MethodCallExpression m = this.parent as MethodCallExpression; if ((b != null && HasEnumInBinaryExpression(b)) || (m != null && m.Method.Name == "HasFlag")) { c = this.ConvertConstantExpressionForEnum(c); ClientEdmModel model = this.context.Model; IEdmType edmType = model.GetOrCreateEdmType(c.Type.IsEnum() ? c.Type : c.Type.GetGenericArguments()[0]); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); string typeNameInEdm = this.context.ResolveNameFromTypeInternal(typeAnnotation.ElementType); MemberInfo member = typeAnnotation.ElementType.GetField(c.Value.ToString()); string memberValue = ClientTypeUtil.GetServerDefinedName(member); ODataEnumValue enumValue = new ODataEnumValue(memberValue, typeNameInEdm ?? typeAnnotation.ElementTypeName); result = ODataUriUtils.ConvertToUriLiteral(enumValue, CommonUtil.ConvertToODataVersion(this.uriVersion), null); } else if (m != null && ReflectionUtil.IsSequenceMethod(m.Method, SequenceMethod.Contains)) { StringBuilder listExpr = new StringBuilder(); ODataVersion version = CommonUtil.ConvertToODataVersion(this.uriVersion); foreach (object item in (IEnumerable)c.Value) { if (listExpr.Length != 0) { listExpr.Append(UriHelper.COMMA); } string uriLiteral = ODataUriUtils.ConvertToUriLiteral(item, version); listExpr.Append(uriLiteral); } // Contains cannot be used with an empty static collection if (listExpr.Length == 0) { throw new InvalidOperationException(Strings.ALinq_ContainsNotValidOnEmptyCollection); } listExpr.Insert(0, UriHelper.LEFTPAREN); listExpr.Append(UriHelper.RIGHTPAREN); result = listExpr.ToString(); } else { try { result = LiteralFormatter.ForConstants.Format(c.Value); } catch (InvalidOperationException) { if (this.cantTranslateExpression) { // there's already a problem in the parents. // we should just return here, because caller somewhere up the stack will throw a better exception return(c); } throw new NotSupportedException(Strings.ALinq_CouldNotConvert(c.Value)); } } Debug.Assert(result != null, "result != null"); this.builder.Append(result); return(c); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">The client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType, ClientEdmModel model) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return(bindingEntityInfo); } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); // Try to get the entity set name from the EntitySetAttribute attributes. In order to make the // inheritance work, we need to look at the attributes declared in the base types also. // EntitySetAttribute does not allow multiples, so there can be at most 1 instance on the type. EntitySetAttribute entitySetAttribute = (EntitySetAttribute)entityType.GetCustomAttributes(typeof(EntitySetAttribute), true).SingleOrDefault(); // There must be exactly one (unambiguous) EntitySetAttribute attribute. bindingEntityInfo.EntitySet = entitySetAttribute != null ? entitySetAttribute.EntitySet : null; bindingEntityInfo.ClientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityType)); foreach (ClientPropertyAnnotation p in bindingEntityInfo.ClientType.Properties()) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.IsStreamLinkProperty) { // DataServiceStreamLink is not mutable externally // It implements INPC to notify controls about our updates // We should ignore its events since we are the only one updating it. continue; } else if (p.IsPrimitiveOrEnumOrComplexCollection) { Debug.Assert(!BindingEntityInfo.IsDataServiceCollection(propertyType, model), "DataServiceCollection cannot be the type that backs collections of primitives or complex types."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection }; } else if (p.IsEntityCollection) { if (BindingEntityInfo.IsDataServiceCollection(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindDataServiceCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexType(propertyType)) { // Add complex types and nothing else. Debug.Assert(!p.IsKnownType, "Known types do not implement INotifyPropertyChanged."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; // For entity types, all of the above types of properties are interesting. // For complex types, only observe collections and complex type properties. if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return(bindingEntityInfo); }
/// <summary> /// Initializes a new instance of the <see cref="FeedAndEntryMaterializerAdapter"/> class. /// </summary> /// <param name="messageReader">The messageReader that is used to get the format of the reader.</param> /// <param name="reader">The reader.</param> /// <param name="model">The model.</param> /// <param name="mergeOption">The mergeOption.</param> internal FeedAndEntryMaterializerAdapter(ODataMessageReader messageReader, ODataReaderWrapper reader, ClientEdmModel model, MergeOption mergeOption) : this(ODataUtils.GetReadFormat(messageReader), reader, model, mergeOption) { }
/// <summary>Verifies the absence of observer for an DataServiceCollection</summary> /// <typeparam name="T">Type of DataServiceCollection</typeparam> /// <param name="oec">Non-typed collection object</param> /// <param name="sourceProperty">Collection property of the source object which is being assigned to</param> /// <param name="sourceType">Type of the source object</param> /// <param name="model">The client model.</param> internal static void VerifyObserverNotPresent <T>(object oec, string sourceProperty, Type sourceType, ClientEdmModel model)
public TestClientContext(Uri serviceRoot, ODataProtocolVersion version, ClientEdmModel model) : base(serviceRoot, version, model) { }
public void Init() { this.serverModel = new EdmModel(); this.serverEntityType = new EdmEntityType("Server.NS", "ServerEntityType"); this.serverEntityTypeName = "Server.NS.ServerEntityType"; this.serverModel.AddElement(this.serverEntityType); this.serverEntityType.AddKeys(this.serverEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32)); var addressType = new EdmComplexType("Server.NS", "Address"); addressType.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String); this.serverEntityType.AddStructuralProperty("Address", new EdmComplexTypeReference(addressType, true)); this.serverComplexTypeName = "Server.NS.Address"; var homeAddressType = new EdmComplexType("Server.NS", "HomeAddress", addressType); homeAddressType.AddStructuralProperty("FamilyName", EdmPrimitiveTypeKind.String); this.serverComplexTypeName = "Server.NS.HomeAddress"; var colorType = new EdmEnumType("Server.NS", "Color"); colorType.AddMember(new EdmEnumMember(colorType, "Red", new EdmIntegerConstant(1))); colorType.AddMember(new EdmEnumMember(colorType, "Blue", new EdmIntegerConstant(2))); colorType.AddMember(new EdmEnumMember(colorType, "Green", new EdmIntegerConstant(3))); this.serverEntityType.AddStructuralProperty("Color", new EdmEnumTypeReference(colorType, false)); this.serverEntityType.AddStructuralProperty("Nicknames", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetInt32(false)))); this.serverEntityType.AddStructuralProperty("OtherAddresses", new EdmCollectionTypeReference(new EdmCollectionType(new EdmComplexTypeReference(addressType, false)))); this.clientModel = new ClientEdmModel(ODataProtocolVersion.V4); this.clientTypeAnnotation = this.clientModel.GetClientTypeAnnotation(typeof(TestClientEntityType)); this.context = new DataServiceContext(new Uri("http://temp/"), ODataProtocolVersion.V4, this.clientModel); this.context.Format.UseJson(this.serverModel); this.testSubject = new ODataPropertyConverter(new RequestInfo(this.context)); this.context.Format.UseJson(this.serverModel); this.context.ResolveName = t => { if (t == typeof(TestClientEntityType)) { return this.serverEntityTypeName; } if (t == typeof(Address)) { return this.serverComplexTypeName; } return null; }; this.entity = new TestClientEntityType { Id = 1, Name = "foo", Number = 1.23f, Address = new Address(), OpenAddress = new Address(), Nicknames = new string[0], OtherAddresses = new[] { new Address() }, Color = 0 }; this.entityWithDerivedComplexProperty = new TestClientEntityType { Id = 1, Name = "foo", Number = 1.23f, Address = new HomeAddress(), OpenAddress = new Address(), Nicknames = new string[0], OtherAddresses = new[] { new Address() } }; this.entityWithDerivedComplexInCollection = new TestClientEntityType { Id = 1, Name = "foo", Number = 1.23f, Address = new HomeAddress(), OpenAddress = new Address(), Nicknames = new string[0], OtherAddresses = new[] { new Address(), new HomeAddress() } }; }
/// <summary> /// Constructor /// </summary> /// <param name="source">Source entity</param> /// <param name="sourceProperty">Navigation property on the source entity</param> /// <param name="target">Target entity</param> /// <param name="model">The client model.</param> internal LinkDescriptor(object source, string sourceProperty, object target, ClientEdmModel model) : this(source, sourceProperty, target, EntityStates.Unchanged) { this.IsSourcePropertyCollection = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, false).IsEntityCollection; }
public void Init() { var clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4); this.typeWithUnserializableProperties = new ClientTypeAnnotation(new EdmEntityType("NS", "Type1"), typeof(EntityWithUnserializableProperties), "NS.Type1", clientEdmModel); this.typeWithPropertiesInStrangeOrder = new ClientTypeAnnotation(new EdmEntityType("NS", "Type2"), typeof(EntityWithPropertiesInStrangeOrder), "NS.Type2", clientEdmModel); }
/// <summary> /// Checks whether the specified expression is allowed in a MethodCall. Expressions that /// produce collections are not allowed. The only exception is when collection property /// belongs to an entity e.g. c.Orders.Select(o => o), where we allow c.Orders. /// </summary> /// <param name="e">Expression to check.</param> /// <param name="model">The client model used.</param> /// <returns>true if expression is disallowed, false otherwise.</returns> internal static bool IsDisallowedExpressionForMethodCall(Expression e, ClientEdmModel model) { // If this is a collection attached to an Entity, then that is fine. MemberExpression me = e as MemberExpression; if (me != null && ClientTypeUtil.TypeIsEntity(me.Expression.Type, model)) { return false; } // All collection producing expressions are disallowed. return IsCollectionProducingExpression(e); }
/// <summary> /// Converts the object to ODataValue, the result could be null, the original primitive object, ODataNullValue, /// ODataEnumValue, ODataCollectionValue, ODataResource, ODataEntityReferenceLinks, ODataEntityReferenceLinks, or /// a list of ODataResource. /// </summary> /// <param name="paramName">The name of the <see cref="UriOperationParameter"/>. Used for error reporting.</param> /// <param name="value">The value of the <see cref="UriOperationParameter"/>.</param> /// <param name="needsSpecialEscaping">True if the result need special escaping.</param> /// <param name="useEntityReference">If true, use entity reference, instead of entity to serialize the parameter.</param> /// <returns>The converted result.</returns> private object ConvertToODataValue(string paramName, object value, ref bool needsSpecialEscaping, bool useEntityReference) { Object valueInODataFormat = null; if (value == null) { needsSpecialEscaping = true; } else if (value is ODataNullValue) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: // Client lib internal conversion to support DateTime if (value is DateTime) { valueInODataFormat = PlatformHelper.ConvertDateTimeToDateTimeOffset((DateTime)value); } else { valueInODataFormat = value; } needsSpecialEscaping = true; break; case EdmTypeKind.Enum: string typeNameInEdm = this.requestInfo.GetServerTypeName(model.GetClientTypeAnnotation(edmType)); valueInODataFormat = new ODataEnumValue( ClientTypeUtil.GetEnumValuesString(value.ToString(), typeAnnotation.ElementType), typeNameInEdm ?? typeAnnotation.ElementTypeName); needsSpecialEscaping = true; break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); valueInODataFormat = ConvertToCollectionValue(paramName, value, itemTypeAnnotation, useEntityReference); break; case EdmTypeKind.Complex: case EdmTypeKind.Entity: Debug.Assert(edmType.TypeKind == EdmTypeKind.Complex || value != null, "edmType.TypeKind == EdmTypeKind.Complex || value != null"); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = ConvertToEntityValue(value, typeAnnotation.ElementType, useEntityReference); break; default: // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } return(valueInODataFormat); }
/// <summary> /// Writes collection value in body operation parameter. /// </summary> /// <param name="parameterWriter">The odata parameter writer.</param> /// <param name="operationParameter">The operation parameter.</param> /// <param name="edmCollectionType">The edm collection type.</param> private void WriteCollectionValueInBodyOperationParameter(ODataParameterWriter parameterWriter, BodyOperationParameter operationParameter, IEdmCollectionType edmCollectionType) { ClientEdmModel model = this.requestInfo.Model; var elementTypeKind = edmCollectionType.ElementType.TypeKind(); if (elementTypeKind == EdmTypeKind.Entity || elementTypeKind == EdmTypeKind.Complex) { ODataWriter feedWriter = parameterWriter.CreateResourceSetWriter(operationParameter.Name); feedWriter.WriteStart(new ODataResourceSet()); IEnumerator enumerator = ((ICollection)operationParameter.Value).GetEnumerator(); while (enumerator.MoveNext()) { Object collectionItem = enumerator.Current; if (collectionItem == null) { if (elementTypeKind == EdmTypeKind.Complex) { feedWriter.WriteStart((ODataResource)null); feedWriter.WriteEnd(); continue; } else { throw new NotSupportedException(Strings.Serializer_NullCollectionParamterItemValue(operationParameter.Name)); } } IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType()); Debug.Assert(edmItemType != null, "edmItemType != null"); if (edmItemType.TypeKind != EdmTypeKind.Entity && edmItemType.TypeKind != EdmTypeKind.Complex) { throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind)); } Debug.Assert(model.GetClientTypeAnnotation(edmItemType).ElementType != null, "edmItemType.GetClientTypeAnnotation().ElementType != null"); ODataResourceWrapper entry = this.CreateODataResourceFromEntityOperationParameter(model.GetClientTypeAnnotation(edmItemType), collectionItem); Debug.Assert(entry != null, "entry != null"); ODataWriterHelper.WriteResource(feedWriter, entry); } feedWriter.WriteEnd(); feedWriter.Flush(); } else { ODataCollectionWriter collectionWriter = parameterWriter.CreateCollectionWriter(operationParameter.Name); ODataCollectionStart odataCollectionStart = new ODataCollectionStart(); collectionWriter.WriteStart(odataCollectionStart); IEnumerator enumerator = ((ICollection)operationParameter.Value).GetEnumerator(); while (enumerator.MoveNext()) { Object collectionItem = enumerator.Current; if (collectionItem == null) { collectionWriter.WriteItem(null); continue; } IEdmType edmItemType = model.GetOrCreateEdmType(collectionItem.GetType()); Debug.Assert(edmItemType != null, "edmItemType != null"); switch (edmItemType.TypeKind) { case EdmTypeKind.Primitive: { object primitiveItemValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(collectionItem, collectionItem.GetType()); collectionWriter.WriteItem(primitiveItemValue); break; } case EdmTypeKind.Enum: { ODataEnumValue enumTmp = this.propertyConverter.CreateODataEnumValue(model.GetClientTypeAnnotation(edmItemType).ElementType, collectionItem, false); collectionWriter.WriteItem(enumTmp); break; } default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(operationParameter.Name, edmItemType.TypeKind)); } } collectionWriter.WriteEnd(); collectionWriter.Flush(); } }
/// <summary> /// gets the UriTranslateResult for a the query /// </summary> /// <param name="model">The client model.</param> /// <returns>an instance of QueryComponents.</returns> internal override QueryComponents QueryComponents(ClientEdmModel model) { return(this.Translate()); }
/// <summary> /// Check to see if the resource to be inserted is a media descriptor, and if so /// setup a POST request for the media content first and turn the rest of /// the operation into a PUT to update the rest of the properties. /// </summary> /// <param name="entityDescriptor">The resource to check/process</param> /// <returns>An instance of ODataRequestMessage to do POST to the media resource</returns> private ODataRequestMessageWrapper CheckAndProcessMediaEntryPost(EntityDescriptor entityDescriptor) { // TODO: Revisit the design of how media link entries are handled during update ClientEdmModel model = this.RequestInfo.Model; ClientTypeAnnotation type = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType())); if (!type.IsMediaLinkEntry && !entityDescriptor.IsMediaLinkEntry) { // this is not a media link descriptor, process normally return(null); } if (type.MediaDataMember == null && entityDescriptor.SaveStream == null) { // The entity is marked as MLE but we don't have the content property // and the user didn't set the save stream. throw Error.InvalidOperation(Strings.Context_MLEWithoutSaveStream(type.ElementTypeName)); } Debug.Assert( (type.MediaDataMember != null && entityDescriptor.SaveStream == null) || (type.MediaDataMember == null && entityDescriptor.SaveStream != null), "Only one way of specifying the MR content is allowed."); ODataRequestMessageWrapper mediaRequest = null; if (type.MediaDataMember != null) { string contentType = null; int contentLength = 0; if (type.MediaDataMember.MimeTypeProperty == null) { contentType = XmlConstants.MimeApplicationOctetStream; } else { object mimeTypeValue = type.MediaDataMember.MimeTypeProperty.GetValue(entityDescriptor.Entity); String mimeType = mimeTypeValue != null?mimeTypeValue.ToString() : null; if (String.IsNullOrEmpty(mimeType)) { throw Error.InvalidOperation( Strings.Context_NoContentTypeForMediaLink( type.ElementTypeName, type.MediaDataMember.MimeTypeProperty.PropertyName)); } contentType = mimeType; } object value = type.MediaDataMember.GetValue(entityDescriptor.Entity); if (value == null) { this.mediaResourceRequestStream = null; } else { byte[] buffer = value as byte[]; if (buffer == null) { string mime; Encoding encoding; ContentTypeUtil.ReadContentType(contentType, out mime, out encoding); if (encoding == null) { encoding = Encoding.UTF8; contentType += XmlConstants.MimeTypeUtf8Encoding; } buffer = encoding.GetBytes(ClientConvert.ToString(value)); } contentLength = buffer.Length; // Need to specify that the buffer is publicly visible as we need to access it later on this.mediaResourceRequestStream = new MemoryStream(buffer, 0, buffer.Length, false, true); } HeaderCollection headers = new HeaderCollection(); headers.SetHeader(XmlConstants.HttpContentLength, contentLength.ToString(CultureInfo.InvariantCulture)); headers.SetHeader(XmlConstants.HttpContentType, contentType); mediaRequest = this.CreateMediaResourceRequest( entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/), XmlConstants.HttpMethodPost, Util.ODataVersion4, type.MediaDataMember == null, // sendChunked true, // applyResponsePreference headers, entityDescriptor); } else { HeaderCollection headers = new HeaderCollection(); this.SetupMediaResourceRequest(headers, entityDescriptor.SaveStream, null /*etag*/); mediaRequest = this.CreateMediaResourceRequest( entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/), XmlConstants.HttpMethodPost, Util.ODataVersion4, type.MediaDataMember == null, // sendChunked true, // applyResponsePreference headers, entityDescriptor); } // Convert the insert into an update for the media link descriptor we just created // (note that the identity still needs to be fixed up on the resbox once // the response comes with the 'location' header; that happens during processing // of the response in SavedResource()) entityDescriptor.State = EntityStates.Modified; return(mediaRequest); }
/// <summary> /// Gets the type that of the instances that will be returned by materializer. /// </summary> /// <param name="expectingPrimitiveValue">Whether the expected is a primitive type.</param> /// <param name="elementType">Actual type on the client.</param> /// <param name="model">The client model used.</param> /// <param name="implementationType">The actual type that implements ICollection<></param> /// <returns>Type of the instances that will be returned by materializer.</returns> /// <remarks> /// For collection navigation properties (i.e. ICollection<T> where T is an entity the method returns T. Materializer will /// return single entity each time MoveNext() is called. However for collection properties we need the whole property instead of just a /// single collection item. /// </remarks> private static Type GetTypeForMaterializer(bool expectingPrimitiveValue, Type elementType, ClientEdmModel model, out Type implementationType) { if (!expectingPrimitiveValue && typeof(IEnumerable).IsAssignableFrom(elementType)) { implementationType = ClientTypeUtil.GetImplementationType(elementType, typeof(ICollection <>)); if (implementationType != null) { Type expectedType = implementationType.GetGenericArguments()[0]; // already know its ICollection<> // We should use the inner type only if this is a collection of entities (as opposed to a collection of primitive or complex types) if (ClientTypeUtil.TypeIsStructured(expectedType, model)) { return(expectedType); } } } implementationType = null; return(elementType); }
/// <summary> /// Determine if the specified type is an DataServiceCollection. /// </summary> /// <remarks> /// If there a generic class in the inheritance hierarchy of the type, that has a single /// entity type paramenter T, and is assignable to DataServiceCollection(Of T), then /// the type is an DataServiceCollection. /// </remarks> /// <param name="collectionType">An object type specifier.</param> /// <param name="model">The client model.</param> /// <returns>true if the type is an DataServiceCollection; otherwise false.</returns> internal static bool IsDataServiceCollection(Type collectionType, ClientEdmModel model) { Debug.Assert(collectionType != null, "Argument 'collectionType' cannot be null."); metadataCacheLock.EnterReadLock(); try { object resultAsObject; if (knownObservableCollectionTypes.TryGetValue(collectionType, out resultAsObject)) { return resultAsObject == TrueObject; } } finally { metadataCacheLock.ExitReadLock(); } Type type = collectionType; bool result = false; while (type != null) { if (type.IsGenericType()) { // Is there a generic class in the inheritance hierarchy, that has a single // entity type paramenter T, and is assignable to DataServiceCollection<T> Type[] parms = type.GetGenericArguments(); if (parms != null && parms.Length == 1 && ClientTypeUtil.TypeOrElementTypeIsEntity(parms[0])) { // if ObservableCollection is not available dataServiceCollection will be null Type dataServiceCollection = WebUtil.GetDataServiceCollectionOfT(parms); if (dataServiceCollection != null && dataServiceCollection.IsAssignableFrom(type)) { result = true; break; } } } type = type.GetBaseType(); } metadataCacheLock.EnterWriteLock(); try { if (!knownObservableCollectionTypes.ContainsKey(collectionType)) { knownObservableCollectionTypes[collectionType] = result ? TrueObject : FalseObject; } } finally { metadataCacheLock.ExitWriteLock(); } return result; }
/// <summary> /// Writes a navigation link. /// </summary> /// <param name="entityDescriptor">The entity</param> /// <param name="relatedLinks">The links related to the entity</param> /// <param name="odataWriter">The ODataWriter used to write the navigation link.</param> internal void WriteNestedResourceInfo(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataWriterWrapper odataWriter) { // TODO: create instance of odatawriter. // TODO: send clientType once, so that we dont need entity descriptor Debug.Assert(EntityStates.Added == entityDescriptor.State, "entity not added state"); Dictionary <string, List <LinkDescriptor> > groupRelatedLinks = new Dictionary <string, List <LinkDescriptor> >(EqualityComparer <string> .Default); foreach (LinkDescriptor end in relatedLinks) { List <LinkDescriptor> linkDescriptorsList = null; if (!groupRelatedLinks.TryGetValue(end.SourceProperty, out linkDescriptorsList)) { linkDescriptorsList = new List <LinkDescriptor>(); groupRelatedLinks.Add(end.SourceProperty, linkDescriptorsList); } linkDescriptorsList.Add(end); } ClientTypeAnnotation clientType = null; foreach (var grlinks in groupRelatedLinks) { if (null == clientType) { ClientEdmModel model = this.requestInfo.Model; clientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType())); } bool isCollection = clientType.GetProperty(grlinks.Key, UndeclaredPropertyBehavior.ThrowException).IsEntityCollection; bool started = false; foreach (LinkDescriptor end in grlinks.Value) { Debug.Assert(!end.ContentGeneratedForSave, "already saved link"); end.ContentGeneratedForSave = true; Debug.Assert(null != end.Target, "null is DELETE"); ODataNestedResourceInfo navigationLink = new ODataNestedResourceInfo(); navigationLink.Url = this.requestInfo.EntityTracker.GetEntityDescriptor(end.Target).GetLatestEditLink(); Debug.Assert(Uri.IsWellFormedUriString(UriUtil.UriToString(navigationLink.Url), UriKind.Absolute), "Uri.IsWellFormedUriString(targetEditLink, UriKind.Absolute)"); navigationLink.IsCollection = isCollection; navigationLink.Name = grlinks.Key; if (!started) { odataWriter.WriteNestedResourceInfoStart(navigationLink); started = true; } odataWriter.WriteNestedResourceInfoStart(navigationLink, end.Source, end.Target); odataWriter.WriteEntityReferenceLink(new ODataEntityReferenceLink() { Url = navigationLink.Url }, end.Source, end.Target); odataWriter.WriteNestedResourceInfoEnd(navigationLink, end.Source, end.Target); } odataWriter.WriteNestedResourceInfoEnd(); } }
/// <summary> /// Determine if the specified type is an entity type. /// </summary> /// <param name="type">An object type specifier.</param> /// <param name="model">The client model.</param> /// <returns>true if the type is an entity type; otherwise false.</returns> internal static bool IsEntityType(Type type, ClientEdmModel model) { Debug.Assert(type != null, "Argument 'type' cannot be null."); metadataCacheLock.EnterReadLock(); try { if (knownNonEntityTypes.Contains(type)) { return false; } } finally { metadataCacheLock.ExitReadLock(); } bool isEntityType; try { if (BindingEntityInfo.IsDataServiceCollection(type, model)) { return false; } isEntityType = ClientTypeUtil.TypeOrElementTypeIsEntity(type); } catch (InvalidOperationException) { isEntityType = false; } if (!isEntityType) { metadataCacheLock.EnterWriteLock(); try { if (!knownNonEntityTypes.Contains(type)) { knownNonEntityTypes.Add(type); } } finally { metadataCacheLock.ExitWriteLock(); } } return isEntityType; }
private string ConvertToEscapedUriValue(string paramName, object value) { Debug.Assert(!string.IsNullOrEmpty(paramName), "!string.IsNullOrEmpty(paramName)"); Object valueInODataFormat = null; // Literal values with single quotes need special escaping due to System.Uri changes in behavior between .NET 4.0 and 4.5. // We need to ensure that our escaped values do not change between those versions, so we need to escape values differently when they could contain single quotes. bool needsSpecialEscaping = false; if (value == null) { needsSpecialEscaping = true; } else { if (value is ODataNullValue) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: valueInODataFormat = value; needsSpecialEscaping = true; break; case EdmTypeKind.Enum: { ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); string typeNameInEdm = this.requestInfo.GetServerTypeName(model.GetClientTypeAnnotation(edmType)); MemberInfo member = typeAnnotation.ElementType.GetMember(value.ToString()).FirstOrDefault(); if (member == null) { throw new NotSupportedException(Strings.Serializer_InvalidEnumMemberValue(typeAnnotation.ElementType.Name, value.ToString())); } string memberValue = ClientTypeUtil.GetServerDefinedName(member); valueInODataFormat = new ODataEnumValue(memberValue, typeNameInEdm ?? typeAnnotation.ElementTypeName); needsSpecialEscaping = true; } break; case EdmTypeKind.Complex: { ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = this.propertyConverter.CreateODataComplexValue(typeAnnotation.ElementType, value, null /*propertyName*/, false /*isCollectionItemType*/, null /*visitedComplexTypeObjects*/); // When using JsonVerbose to format query string parameters for Actions, // we cannot write out Complex values in the URI without the type name of the complex type in the JSON payload. // If this value is null, the client has to set the ResolveName property on the DataServiceContext instance. ODataComplexValue complexValue = (ODataComplexValue)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } } break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); switch (itemTypeAnnotation.EdmType.TypeKind) { // We only support primitive, Enum or complex type as a collection item type. case EdmTypeKind.Primitive: case EdmTypeKind.Enum: case EdmTypeKind.Complex: break; default: throw new NotSupportedException(Strings.Serializer_InvalidCollectionParamterItemType(paramName, itemTypeAnnotation.EdmType.TypeKind)); } valueInODataFormat = this.propertyConverter.CreateODataCollection( itemTypeAnnotation.ElementType, null /*propertyName*/, value, null /*visitedComplexTypeObjects*/); break; default: // EdmTypeKind.Entity // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } // When calling Execute() to invoke an Action, the client doesn't support parsing the target url // to determine which IEdmOperationImport to pass to the ODL writer. So the ODL writer is // serializing the parameter payload without metadata. Setting the model to null so ODL doesn't // do unecessary validations when writing without metadata. string literal = ODataUriUtils.ConvertToUriLiteral(valueInODataFormat, CommonUtil.ConvertToODataVersion(this.requestInfo.MaxProtocolVersionAsVersion), null /* edmModel */); // The value from ConvertToUriValue will not be escaped, but will already contain literal delimiters like single quotes, so we // need to use our own escape method that will preserve those characters instead of directly calling Uri.EscapeDataString that may escape them. // This is only necessary for primitives and nulls because the other structures are serialized using the JSON format and it uses double quotes // which have always been escaped. if (needsSpecialEscaping) { return(DataStringEscapeBuilder.EscapeDataString(literal)); } return(Uri.EscapeDataString(literal)); }
/// <summary> /// Tries to get the value of a property and corresponding BindingPropertyInfo or ClientPropertyAnnotation if the property exists /// </summary> /// <param name="source">Source object whose property needs to be read</param> /// <param name="sourceProperty">Name of the source object property</param> /// <param name="model">The client model.</param> /// <param name="bindingPropertyInfo">BindingPropertyInfo corresponding to <paramref name="sourceProperty"/></param> /// <param name="clientProperty">Instance of ClientProperty corresponding to <paramref name="sourceProperty"/></param> /// <param name="propertyValue">Value of the property</param> /// <returns>true if the property exists and the value was read; otherwise false.</returns> internal static bool TryGetPropertyValue(object source, string sourceProperty, ClientEdmModel model, out BindingPropertyInfo bindingPropertyInfo, out ClientPropertyAnnotation clientProperty, out object propertyValue) { Type sourceType = source.GetType(); bindingPropertyInfo = BindingEntityInfo.GetObservableProperties(sourceType, model) .SingleOrDefault(x => x.PropertyInfo.PropertyName == sourceProperty); bool propertyFound = bindingPropertyInfo != null; // bindingPropertyInfo is null for primitive properties. if (!propertyFound) { clientProperty = BindingEntityInfo.GetClientType(sourceType, model) .GetProperty(sourceProperty, true); propertyFound = clientProperty != null; if (!propertyFound) { propertyValue = null; } else { propertyValue = clientProperty.GetValue(source); } } else { clientProperty = null; propertyValue = bindingPropertyInfo.PropertyInfo.GetValue(source); } return propertyFound; }
/// <summary> /// Tries to get the value of a property and corresponding BindingPropertyInfo or ClientPropertyAnnotation if the property exists /// </summary> /// <param name="source">Source object whose property needs to be read</param> /// <param name="sourceProperty">Name of the source object property</param> /// <param name="model">The client model.</param> /// <param name="bindingPropertyInfo">BindingPropertyInfo corresponding to <paramref name="sourceProperty"/></param> /// <param name="clientProperty">Instance of ClientProperty corresponding to <paramref name="sourceProperty"/></param> /// <param name="propertyValue">Value of the property</param> /// <returns>true if the property exists and the value was read; otherwise false.</returns> internal static bool TryGetPropertyValue(object source, string sourceProperty, ClientEdmModel model, out BindingPropertyInfo bindingPropertyInfo, out ClientPropertyAnnotation clientProperty, out object propertyValue) { Type sourceType = source.GetType(); bindingPropertyInfo = BindingEntityInfo.GetObservableProperties(sourceType, model) .SingleOrDefault(x => x.PropertyInfo.PropertyName == sourceProperty); bool propertyFound = bindingPropertyInfo != null; // bindingPropertyInfo is null for primitive properties. if (!propertyFound) { clientProperty = BindingEntityInfo.GetClientType(sourceType, model) .GetProperty(sourceProperty, true); propertyFound = clientProperty != null; if (!propertyFound) { propertyValue = null; } else { propertyValue = clientProperty.GetValue(source); } } else { clientProperty = null; propertyValue = bindingPropertyInfo.PropertyInfo.GetValue(source); } return(propertyFound); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">The client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> private static BindingEntityInfoPerType GetBindingEntityInfoFor(Type entityType, ClientEdmModel model) { BindingEntityInfoPerType bindingEntityInfo; metadataCacheLock.EnterReadLock(); try { if (bindingEntityInfos.TryGetValue(entityType, out bindingEntityInfo)) { return bindingEntityInfo; } } finally { metadataCacheLock.ExitReadLock(); } bindingEntityInfo = new BindingEntityInfoPerType(); // Try to get the entity set name from the EntitySetAttribute attributes. In order to make the // inheritance work, we need to look at the attributes declared in the base types also. // EntitySetAttribute does not allow multiples, so there can be at most 1 instance on the type. EntitySetAttribute entitySetAttribute = (EntitySetAttribute)entityType.GetCustomAttributes(typeof(EntitySetAttribute), true).SingleOrDefault(); // There must be exactly one (unambiguous) EntitySetAttribute attribute. bindingEntityInfo.EntitySet = entitySetAttribute != null ? entitySetAttribute.EntitySet : null; bindingEntityInfo.ClientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityType)); foreach (ClientPropertyAnnotation p in bindingEntityInfo.ClientType.Properties()) { BindingPropertyInfo bpi = null; Type propertyType = p.PropertyType; if (p.IsStreamLinkProperty) { // DataServiceStreamLink is not mutable externally // It implements INPC to notify controls about our updates // We should ignore its events since we are the only one updating it. continue; } else if (p.IsPrimitiveOrEnumOrComplexCollection) { Debug.Assert(!BindingEntityInfo.IsDataServiceCollection(propertyType, model), "DataServiceCollection cannot be the type that backs collections of primitives or complex types."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection }; } else if (p.IsEntityCollection) { if (BindingEntityInfo.IsDataServiceCollection(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindDataServiceCollection }; } } else if (BindingEntityInfo.IsEntityType(propertyType, model)) { bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindEntity }; } else if (BindingEntityInfo.CanBeComplexType(propertyType)) { // Add complex types and nothing else. Debug.Assert(!p.IsKnownType, "Known types do not implement INotifyPropertyChanged."); bpi = new BindingPropertyInfo { PropertyKind = BindingPropertyKind.BindingPropertyKindComplex }; } if (bpi != null) { bpi.PropertyInfo = p; // For entity types, all of the above types of properties are interesting. // For complex types, only observe collections and complex type properties. if (bindingEntityInfo.ClientType.IsEntityType || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindComplex || bpi.PropertyKind == BindingPropertyKind.BindingPropertyKindPrimitiveOrComplexCollection) { bindingEntityInfo.ObservableProperties.Add(bpi); } } } metadataCacheLock.EnterWriteLock(); try { if (!bindingEntityInfos.ContainsKey(entityType)) { bindingEntityInfos[entityType] = bindingEntityInfo; } } finally { metadataCacheLock.ExitWriteLock(); } return bindingEntityInfo; }
/// <summary>Gets entity set corresponding to a given type</summary> /// <param name="entityType">Intput type</param> /// <param name="model">The client model.</param> /// <returns>Entity set name for the type</returns> private static string GetEntitySetAttribute(Type entityType, ClientEdmModel model) { return(GetBindingEntityInfoFor(entityType, model).EntitySet); }
/// <summary>Gets entity set corresponding to a given type</summary> /// <param name="entityType">Intput type</param> /// <param name="model">The client model.</param> /// <returns>Entity set name for the type</returns> private static string GetEntitySetAttribute(Type entityType, ClientEdmModel model) { return GetBindingEntityInfoFor(entityType, model).EntitySet; }
/// <summary>Gets the ClientType corresponding to the given type</summary> /// <param name="entityType">Input type</param> /// <param name="model">The client model.</param> /// <returns>Corresponding ClientType</returns> internal static ClientTypeAnnotation GetClientType(Type entityType, ClientEdmModel model) { return(GetBindingEntityInfoFor(entityType, model).ClientType); }
/// <summary>Obtain binding info corresponding to a given type</summary> /// <param name="entityType">Type for which to obtain information</param> /// <param name="model">the client model.</param> /// <returns>Info about the <paramref name="entityType"/></returns> internal static IList<BindingPropertyInfo> GetObservableProperties(Type entityType, ClientEdmModel model) { return GetBindingEntityInfoFor(entityType, model).ObservableProperties; }
/// <summary> /// Validates the specified <paramref name="property"/> matches /// the parsed <paramref name="atomProperty"/>. /// </summary> /// <param name="property">Property as understood by the type system.</param> /// <param name="atomProperty">Property as parsed.</param> /// <param name="model">Client model.</param> /// <param name="performEntityCheck">whether to do the entity check or not.</param> internal static void ValidatePropertyMatch(ClientPropertyAnnotation property, ODataProperty atomProperty, ClientEdmModel model, bool performEntityCheck) { Debug.Assert(property != null, "property != null"); Debug.Assert(atomProperty != null, "atomProperty != null"); ODataFeed feed = atomProperty.Value as ODataFeed; ODataEntry entry = atomProperty.Value as ODataEntry; if (property.IsKnownType && (feed != null || entry != null)) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkLocalSimple); } Type propertyType = null; if (feed != null) { // We need to fail if the payload states that the property is a navigation collection property // and in the client, the property is not a collection property. if (!property.IsEntityCollection) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName)); } propertyType = property.EntityCollectionItemType; } if (entry != null) { if (property.IsEntityCollection) { throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName)); } propertyType = property.PropertyType; } // If the server type and the client type does not match, we need to throw. // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa if (propertyType != null && performEntityCheck) { if (!ClientTypeUtil.TypeIsEntity(propertyType, model)) { throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(propertyType.ToString())); } } }
/// <summary>Gets the ClientType corresponding to the given type</summary> /// <param name="entityType">Input type</param> /// <param name="model">The client model.</param> /// <returns>Corresponding ClientType</returns> internal static ClientTypeAnnotation GetClientType(Type entityType, ClientEdmModel model) { return GetBindingEntityInfoFor(entityType, model).ClientType; }
/// <summary> /// Creates and returns an ODataComplexValue from the given value. /// </summary> /// <param name="complexType">The value type.</param> /// <param name="value">The complex value.</param> /// <param name="propertyName">If the value is a property, then it represents the name of the property. Can be null, for non-property.</param> /// <param name="isCollectionItem">True, if the value is an item in a collection, false otherwise.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <returns>An ODataComplexValue representing the given value.</returns> internal ODataComplexValue CreateODataComplexValue(Type complexType, object value, string propertyName, bool isCollectionItem, HashSet <object> visitedComplexTypeObjects) { Debug.Assert(complexType != null, "complexType != null"); Debug.Assert(value != null || !isCollectionItem, "Collection items must not be null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation complexTypeAnnotation = model.GetClientTypeAnnotation(complexType); Debug.Assert(complexTypeAnnotation != null, "complexTypeAnnotation != null"); Debug.Assert(!complexTypeAnnotation.IsEntityType, "Unexpected entity"); // Handle null values for complex types by putting m:null="true" if (value == null) { Debug.Assert(!isCollectionItem, "Null collection items are not supported. Should have already been checked."); return(null); } if (visitedComplexTypeObjects == null) { visitedComplexTypeObjects = new HashSet <object>(ReferenceEqualityComparer <object> .Instance); } else if (visitedComplexTypeObjects.Contains(value)) { if (propertyName != null) { throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName)); } else { Debug.Assert(complexTypeAnnotation.ElementTypeName != null, "complexTypeAnnotation.ElementTypeName != null"); throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInNonPropertyComplexTypes(complexTypeAnnotation.ElementTypeName)); } } visitedComplexTypeObjects.Add(value); ODataComplexValue odataComplexValue = new ODataComplexValue(); // When TypeName is set, it causes validation to occur when ODataLib writes out the collection. Part of the validation ensures that all items // in the collection are exactly the same type, no derived types are allowed. In the released WCF Data Services 5.0 implementation, we don't set // TypeName here, so that validation does not occur, therefore we will set this value only for JSON Light, so we don't break existing code. if (!this.requestInfo.Format.UsingAtom) { odataComplexValue.TypeName = complexTypeAnnotation.ElementTypeName; } string serverTypeName = this.requestInfo.GetServerTypeName(complexTypeAnnotation); // If this complex type is a collection item don't put type name on each item if (!isCollectionItem) { odataComplexValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName }); } odataComplexValue.Properties = this.PopulateProperties(value, serverTypeName, complexTypeAnnotation.PropertiesToSerialize(), visitedComplexTypeObjects); visitedComplexTypeObjects.Remove(value); return(odataComplexValue); }
/// <summary> /// Get the entity set name for the target entity object. /// </summary> /// <param name="target">An entity object.</param> /// <param name="targetEntitySet">The 'currently known' entity set name for the target object.</param> /// <param name="model">The client model.</param> /// <returns>The entity set name for the target object.</returns> /// <remarks> /// Allow user code to provide the entity set name. If user code does not provide the entity set name, then /// this method will get the entity set name from the value of the EntitySetAttribute. /// The 'currently known' entity set name for top level collections can be provided through OEC constructor /// </remarks> internal static string GetEntitySet( object target, string targetEntitySet, ClientEdmModel model) { Debug.Assert(target != null, "Argument 'target' cannot be null."); Debug.Assert(BindingEntityInfo.IsEntityType(target.GetType(), model), "Argument 'target' must be an entity type."); // Here's the rules in order of priority for resolving entity set name // 1. EntitySet name passed in the constructor or extension methods of DataServiceCollection // 2. EntitySet name specified in the EntitySet attribute by the code gen. {Remember this attribute is // not generated in case of MEST) if (!String.IsNullOrEmpty(targetEntitySet)) { return targetEntitySet; } else { // If there is not a 'currently known' entity set name to validate against, then there must be // EntitySet attribute on the entity type return BindingEntityInfo.GetEntitySetAttribute(target.GetType(), model); } }
/// <summary> /// Creates a new instance of EntityTracker class which tracks all instances of entities and links tracked by the context. /// </summary> /// <param name="maxProtocolVersion">max protocol version that the client understands.</param> internal EntityTracker(ClientEdmModel maxProtocolVersion) { this.model = maxProtocolVersion; }
/// <summary> /// Create a new instance of Entity descriptor. /// </summary> /// <param name="model">The client model</param> internal EntityDescriptor(ClientEdmModel model) : base(EntityStates.Unchanged) { this.Model = model; this.PropertiesToSerialize = new HashSet <string>(StringComparer.Ordinal); }
/// <summary>The QueryComponents associated with this request</summary> /// <param name="model">The client model.</param> /// <returns>instance of query components</returns> internal abstract QueryComponents QueryComponents(ClientEdmModel model);
/// <summary> /// Converts the object to ODataValue, the result could be null, the original primitive object, ODataNullValue, /// ODataEnumValue, ODataCollectionValue, ODataEntry, ODataEntityReferenceLinks, ODataEntityReferenceLinks, or /// a list of ODataEntry. /// </summary> /// <param name="paramName">The name of the <see cref="UriOperationParameter"/>. Used for error reporting.</param> /// <param name="value">The value of the <see cref="UriOperationParameter"/>.</param> /// <param name="needsSpecialEscaping">True if the result need special escaping.</param> /// <param name="useEntityReference">If true, use entity reference, instead of entity to serialize the parameter.</param> /// <returns>The converted result.</returns> private object ConvertToODataValue(string paramName, object value, ref bool needsSpecialEscaping, bool useEntityReference) { Object valueInODataFormat = null; if (value == null) { needsSpecialEscaping = true; } else if (value is ODataNullValue) { valueInODataFormat = value; needsSpecialEscaping = true; } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(value.GetType()); Debug.Assert(edmType != null, "edmType != null"); ClientTypeAnnotation typeAnnotation = model.GetClientTypeAnnotation(edmType); Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); switch (edmType.TypeKind) { case EdmTypeKind.Primitive: valueInODataFormat = value; needsSpecialEscaping = true; break; case EdmTypeKind.Enum: string typeNameInEdm = this.requestInfo.GetServerTypeName(model.GetClientTypeAnnotation(edmType)); valueInODataFormat = new ODataEnumValue( ClientTypeUtil.GetEnumValuesString(value.ToString(), typeAnnotation.ElementType), typeNameInEdm ?? typeAnnotation.ElementTypeName); needsSpecialEscaping = true; break; case EdmTypeKind.Complex: Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = this.propertyConverter.CreateODataComplexValue(typeAnnotation.ElementType, value, null, false, null); // When using JsonVerbose to format query string parameters for Actions, // we cannot write out Complex values in the URI without the type name of the complex type in the JSON payload. // If this value is null, the client has to set the ResolveName property on the DataServiceContext instance. ODataComplexValue complexValue = (ODataComplexValue)valueInODataFormat; SerializationTypeNameAnnotation serializedTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializedTypeNameAnnotation == null || string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName)) { throw Error.InvalidOperation(Strings.DataServiceException_GeneralError); } break; case EdmTypeKind.Collection: IEdmCollectionType edmCollectionType = edmType as IEdmCollectionType; Debug.Assert(edmCollectionType != null, "edmCollectionType != null"); IEdmTypeReference itemTypeReference = edmCollectionType.ElementType; Debug.Assert(itemTypeReference != null, "itemTypeReference != null"); ClientTypeAnnotation itemTypeAnnotation = model.GetClientTypeAnnotation(itemTypeReference.Definition); Debug.Assert(itemTypeAnnotation != null, "itemTypeAnnotation != null"); valueInODataFormat = ConvertToCollectionValue(paramName, value, itemTypeAnnotation, useEntityReference); break; case EdmTypeKind.Entity: Debug.Assert(typeAnnotation != null, "typeAnnotation != null"); valueInODataFormat = ConvertToEntityValue(value, typeAnnotation.ElementType, useEntityReference); break; default: // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(paramName, edmType.TypeKind)); } Debug.Assert(valueInODataFormat != null, "valueInODataFormat != null"); } return(valueInODataFormat); }
/// <summary> /// Creates a new instance of EntityTracker class which tracks all instances of entities and links tracked by the context. /// </summary> /// <param name="maxProtocolVersion">max protocol version that the client understands.</param> internal EntityTracker(ClientEdmModel maxProtocolVersion) { this.model = maxProtocolVersion; }
/// <summary> /// Writes the body operation parameters associated with a ServiceAction. For each BodyOperationParameter: /// 1. calls ODataPropertyConverter to convert CLR object into ODataValue/primitive values. /// 2. then calls ODataParameterWriter to write the ODataValue/primitive values. /// </summary> /// <param name="operationParameters">The list of operation parameters to write.</param> /// <param name="requestMessage">The OData request message used to write the operation parameters.</param> internal void WriteBodyOperationParameters(List <BodyOperationParameter> operationParameters, ODataRequestMessageWrapper requestMessage) { Debug.Assert(requestMessage != null, "requestMessage != null"); Debug.Assert(operationParameters != null, "operationParameters != null"); Debug.Assert(operationParameters.Any(), "operationParameters.Any()"); using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, true /*isParameterPayload*/)) { ODataParameterWriter parameterWriter = messageWriter.CreateODataParameterWriter(null); parameterWriter.WriteStart(); foreach (BodyOperationParameter operationParameter in operationParameters) { if (operationParameter.Value == null) { parameterWriter.WriteValue(operationParameter.Name, operationParameter.Value); } else { ClientEdmModel model = this.requestInfo.Model; IEdmType edmType = model.GetOrCreateEdmType(operationParameter.Value.GetType()); Debug.Assert(edmType != null, "edmType != null"); switch (edmType.TypeKind) { case EdmTypeKind.Collection: { this.WriteCollectionValueInBodyOperationParameter(parameterWriter, operationParameter, (IEdmCollectionType)edmType); break; } case EdmTypeKind.Complex: case EdmTypeKind.Entity: { Debug.Assert(model.GetClientTypeAnnotation(edmType).ElementType != null, "model.GetClientTypeAnnotation(edmType).ElementType != null"); ODataResourceWrapper entry = this.CreateODataResourceFromEntityOperationParameter(model.GetClientTypeAnnotation(edmType), operationParameter.Value); Debug.Assert(entry != null, "entry != null"); var entryWriter = parameterWriter.CreateResourceWriter(operationParameter.Name); ODataWriterHelper.WriteResource(entryWriter, entry); break; } case EdmTypeKind.Primitive: object primitiveValue = ODataPropertyConverter.ConvertPrimitiveValueToRecognizedODataType(operationParameter.Value, operationParameter.Value.GetType()); parameterWriter.WriteValue(operationParameter.Name, primitiveValue); break; case EdmTypeKind.Enum: ODataEnumValue tmp = this.propertyConverter.CreateODataEnumValue( model.GetClientTypeAnnotation(edmType).ElementType, operationParameter.Value, false); parameterWriter.WriteValue(operationParameter.Name, tmp); break; default: // EdmTypeKind.Row // EdmTypeKind.EntityReference throw new NotSupportedException(Strings.Serializer_InvalidParameterType(operationParameter.Name, edmType.TypeKind)); } } // else } // foreach parameterWriter.WriteEnd(); parameterWriter.Flush(); } }