/// <summary> /// Creates an <see cref="IEdmModel"/> based on a combination of the ObjectContext's properties and it's metadata workspace /// </summary> /// <param name="context">The object context instance</param> /// <returns>An <see cref="IEdmModel"/> based on a combination of the ObjectContext's properties and it's metadata workspace</returns> public static IEdmModel CreateModel(ObjectContext context) { Assembly objectLayerAssembly = context.GetType().Assembly; ConstructableMetadata metadata = new ConstructableMetadata(context.DefaultContainerName, ("DBNorthwindModel")); foreach (EntityType entityType in context.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace)) { Type clrType = objectLayerAssembly.GetType("Microsoft.Test.Taupo.OData.WCFService.Model." + entityType.Name, false, true); IEdmEntityType edmEntityType = metadata.AddEntityType(entityType.Name, clrType, null, false, "DBNorthwindModel"); AddProperties(metadata, entityType.Properties, edmEntityType); } foreach (var entitySet in context.GetType().GetProperties().Where(property => property.PropertyType.GetInterface("IQueryable") != null)) { Type entityType = entitySet.PropertyType.GetGenericArguments().First(); IEdmEntityType edmEntityType = metadata.FindType("DBNorthwindModel." + entityType.Name) as IEdmEntityType; metadata.AddEntitySet(entitySet.Name, edmEntityType); } return metadata; }
/// <summary> /// Adds Associations to the EDM model based on an Entity Data Model Schema /// </summary> /// <param name="model">EDM model to append to</param> /// <param name="model">Entity Data Model Schema to get associations from</param> private static void AddNavigationProperties(ConstructableMetadata model, IEdmEntityType entityType, IEnumerable<NavigationProperty> properties) { foreach (NavigationProperty property in properties) { if (property.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) { model.AddResourceSetReferenceProperty( entityType, property.Name, model.FindEntitySet(property.ToEndMember.GetEntityType().Name), model.FindType(property.ToEndMember.GetEntityType().FullName) as IEdmEntityType ); } else { model.AddResourceReferenceProperty( entityType, property.Name, model.FindEntitySet(property.ToEndMember.GetEntityType().Name), model.FindType(property.ToEndMember.GetEntityType().FullName) as IEdmEntityType ); } } }
/// <summary> /// Adds a Primitive Property to <paramref name="metadata"/> /// </summary> /// <param name="metadata">Metadata document to be added to</param> /// <param name="type">The type of the primitive property</param> /// <param name="property">Property to add</param> /// <param name="structuredType">Structured type to add property to</param> private static void AddPrimitiveProperty(ConstructableMetadata metadata, Type type, EdmProperty property, IEdmStructuredType structuredType) { if (property.DeclaringType is EntityType && ((EntityType)property.DeclaringType).KeyMembers.Contains(property.Name)) { metadata.AddKeyProperty(structuredType, property.Name, type, IsETagProperty(property)); } else if (type == typeof(Stream)) { metadata.AddNamedStreamProperty(structuredType, property.Name); } else { metadata.AddPrimitiveProperty(structuredType, property.Name, type, IsETagProperty(property)); } }
/// <summary> /// Adds the <paramref name="properties"/> to the <paramref name="metadata"/> /// Recursively calls self to add complex types as needed /// </summary> /// <param name="metadata">The metadata document to add properties to</param> /// <param name="properties">The properties to add to the metadata document</param> /// <param name="structuredType">The type that these elements belong to</param> private static void AddProperties(ConstructableMetadata metadata, IEnumerable<EdmProperty> properties, IEdmStructuredType structuredType) { foreach (EdmProperty property in properties) { AddProperty(metadata, property, structuredType); } }
/// <summary> /// Gets the edm model. Constructs it if it doesn't exist /// </summary> /// <returns>Edm model</param> public IEdmModel GetModel() { if (this.model == null) { ConstructableMetadata metadata = new ConstructableMetadata("InMemoryEntities", "Microsoft.Test.Taupo.OData.WCFService"); IEdmComplexType addressType = metadata.AddComplexType("Address", typeof(Address), null, false); metadata.AddPrimitiveProperty(addressType, "Street", typeof(string)); metadata.AddPrimitiveProperty(addressType, "City", typeof(string)); metadata.AddPrimitiveProperty(addressType, "PostalCode", typeof(string)); IEdmComplexType homeAddressType = metadata.AddComplexType("HomeAddress", typeof(HomeAddress), addressType, false); metadata.AddPrimitiveProperty(homeAddressType, "HomeNO", typeof(string)); IEdmEntityType personType = metadata.AddEntityType("Person", typeof(Person), null, false, "Microsoft.Test.Taupo.OData.WCFService"); metadata.AddKeyProperty(personType, "PersonID", typeof(Int32)); metadata.AddPrimitiveProperty(personType, "FirstName", typeof(string)); metadata.AddPrimitiveProperty(personType, "LastName", typeof(string)); metadata.AddComplexProperty(personType, "HomeAddress", addressType); metadata.AddPrimitiveProperty(personType, "Home", typeof(GeographyPoint)); metadata.AddMultiValueProperty(personType, "Numbers", typeof(string)); metadata.AddContainedResourceSetReferenceProperty(personType, "Brother", personType); metadata.AddContainedResourceReferenceProperty(personType, "Child", personType); var peopleset = metadata.AddEntitySet("People", personType); var specialPerson = metadata.AddSingleton("SpecialPerson", personType); IEdmEntityType customerType = metadata.AddEntityType("Customer", typeof(Customer), personType, false, "Microsoft.Test.Taupo.OData.WCFService"); metadata.AddPrimitiveProperty(customerType, "City", typeof(string)); metadata.AddPrimitiveProperty(customerType, "Birthday", typeof(DateTimeOffset)); metadata.AddPrimitiveProperty(customerType, "TimeBetweenLastTwoOrders", typeof(TimeSpan)); var customerset = metadata.AddEntitySet("Customers", customerType); var vipCustomer = metadata.AddSingleton("VipCustomer", customerType); IEdmEntityType employeeType = metadata.AddEntityType("Employee", typeof(Employee), personType, false, "Microsoft.Test.Taupo.OData.WCFService", true); metadata.AddPrimitiveProperty(employeeType, "DateHired", typeof(DateTimeOffset)); metadata.AddPrimitiveProperty(employeeType, "Office", typeof(GeographyPoint)); var employeeset = metadata.AddEntitySet("Employees", employeeType); var boss = metadata.AddSingleton("Boss", employeeType); IEdmEntityType productType = metadata.AddEntityType("Product", typeof(Product), null, false, "Microsoft.Test.Taupo.OData.WCFService"); metadata.AddKeyProperty(productType, "ProductID", typeof(Int32)); metadata.AddPrimitiveProperty(productType, "Name", typeof(string)); metadata.AddPrimitiveProperty(productType, "QuantityPerUnit", typeof(string)); metadata.AddPrimitiveProperty(productType, "UnitPrice", typeof(float)); metadata.AddPrimitiveProperty(productType, "QuantityInStock", typeof(Int32)); metadata.AddPrimitiveProperty(productType, "Discontinued", typeof(bool)); metadata.AddComplexProperty(productType, "ManufactureAddresss", addressType, true); var productset = metadata.AddEntitySet("Products", productType); var specialProduct = metadata.AddSingleton("SpecialProduct", productType); IEdmEntityType orderType = metadata.AddEntityType("Order", typeof(Order), null, false, "Microsoft.Test.Taupo.OData.WCFService"); var orderset = metadata.AddEntitySet("Orders", orderType); metadata.AddKeyProperty(orderType, "OrderID", typeof(Int32)); metadata.AddPrimitiveProperty(orderType, "CustomerID", typeof(Int32)); metadata.AddPrimitiveProperty(orderType, "EmployeeID", typeof(Int32?)); metadata.AddPrimitiveProperty(orderType, "OrderDate", typeof(DateTimeOffset)); metadata.AddResourceReferenceProperty(orderType, "LoggedInEmployee", employeeset, null); metadata.AddResourceReferenceProperty(orderType, "CustomerForOrder", customerset, null); var specialOrder = metadata.AddSingleton("SpecialOrder", orderType); metadata.AddContainedResourceReferenceProperty(personType, "FirstOrder", orderType); IEdmEntityType orderDetailType = metadata.AddEntityType("OrderDetail", typeof(OrderDetail), null, false, "Microsoft.Test.Taupo.OData.WCFService"); metadata.AddKeyProperty(orderDetailType, "OrderID", typeof(Int32)); metadata.AddKeyProperty(orderDetailType, "ProductID", typeof(Int32)); metadata.AddPrimitiveProperty(orderDetailType, "OrderPlaced", typeof(DateTimeOffset)); metadata.AddPrimitiveProperty(orderDetailType, "Quantity", typeof(Int32)); metadata.AddPrimitiveProperty(orderDetailType, "UnitPrice", typeof(float)); var productOrderedNavigation = metadata.AddResourceReferenceProperty(orderDetailType, "ProductOrdered", productset, null); var associatedOrderNavigation = metadata.AddResourceReferenceProperty(orderDetailType, "AssociatedOrder", orderset, null); var orderdetailsSet = metadata.AddEntitySet("OrderDetails", orderDetailType); // Edm.Duration IEdmEntityType durationInKeyType = metadata.AddEntityType("DurationInKey", typeof(DurationInKey), null, false, "Microsoft.Test.Taupo.OData.WCFService"); metadata.AddKeyProperty(durationInKeyType, "Id", typeof(TimeSpan)); metadata.AddEntitySet("DurationInKeys", durationInKeyType); // FUNCTIONS // Function that binds to single order metadata.AddFunctionAndFunctionImport("GetOrderRate", orderType.ToTypeReference(), MetadataUtils.GetPrimitiveTypeReference(typeof (Int32)), null, true); //Function that binds to a single order and returns a single order metadata.AddFunction("GetNextOrder", orderType.ToTypeReference(), orderType.ToTypeReference(), true, new EdmPathExpression("bindingparameter"), true); // Function that returns a set of orders var collectionOrders = new EdmCollectionType(orderType.ToTypeReference()).ToTypeReference(); metadata.AddFunction("OrdersWithMoreThanTwoItems", collectionOrders, collectionOrders, true, new EdmPathExpression("bindingparameter"), true /*iscomposable*/); var overload1Function = metadata.AddFunction("OrdersWithMoreThanTwoItems", collectionOrders, collectionOrders, true, new EdmPathExpression("bindingparameter"), true /*iscomposable*/); overload1Function.AddParameter("IntParameter", MetadataUtils.GetPrimitiveTypeReference(typeof (Int32))); var overload2Function = metadata.AddFunction("OrdersWithMoreThanTwoItems", collectionOrders, collectionOrders, true, new EdmPathExpression("bindingparameter"), true /*iscomposable*/); overload2Function.AddParameter("IntParameter", MetadataUtils.GetPrimitiveTypeReference(typeof(Int32))); overload2Function.AddParameter("EntityParameter", productType.ToTypeReference()); var collectionCustomers = new EdmCollectionType(customerType.ToTypeReference()).ToTypeReference(); var customersInCityFunction = metadata.AddFunction("InCity", collectionCustomers, collectionCustomers, true, new EdmPathExpression("bindingparameter"), true /*iscomposable*/); customersInCityFunction.AddParameter("City", MetadataUtils.GetPrimitiveTypeReference(typeof(String))); var customersWithinFunction = metadata.AddFunction("Within", collectionCustomers, collectionCustomers, true, new EdmPathExpression("bindingparameter"), true /*iscomposable*/); customersWithinFunction.AddParameter("Location", MetadataUtils.GetPrimitiveTypeReference(typeof(GeographyPoint))); customersWithinFunction.AddParameter("Address", addressType.ToTypeReference(true /*nullable*/)); customersWithinFunction.AddParameter("Distance", MetadataUtils.GetPrimitiveTypeReference(typeof(Double))); customersWithinFunction.AddParameter("ArbitraryInt", MetadataUtils.GetPrimitiveTypeReference(typeof(Int32))); customersWithinFunction.AddParameter("DateTimeOffset", MetadataUtils.GetPrimitiveTypeReference(typeof(DateTimeOffset?))); customersWithinFunction.AddParameter("Byte", MetadataUtils.GetPrimitiveTypeReference(typeof(Byte))); customersWithinFunction.AddParameter("LineString", MetadataUtils.GetPrimitiveTypeReference(typeof(GeometryLineString))); var withinFunction = metadata.AddFunction("Within", customerType.ToTypeReference(), MetadataUtils.GetPrimitiveTypeReference(typeof(bool)), true, null, true /*iscomposable*/); withinFunction.AddParameter("Location", addressType.ToTypeReference()); withinFunction.AddParameter("Distance", MetadataUtils.GetPrimitiveTypeReference(typeof(Int32))); var withinFunction2 = metadata.AddFunction("Within", customerType.ToTypeReference(), MetadataUtils.GetPrimitiveTypeReference(typeof(bool)), true, null, true /*iscomposable*/); withinFunction2.AddParameter("Distance", MetadataUtils.GetPrimitiveTypeReference(typeof(Int32))); metadata.AddFunction("GetChild", personType.ToTypeReference(), personType.ToTypeReference(), true, new EdmPathExpression("bindingparameter/Child"), true /*iscomposable*/); metadata.AddAction("GetBrothers", personType.ToTypeReference(), new EdmCollectionTypeReference(new EdmCollectionType(personType.ToTypeReference())), true, new EdmPathExpression("bindingparameter/Child")); //Unbound Functions var lotsofOrders = metadata.AddFunctionAndFunctionImport("HasLotsOfOrders", null, MetadataUtils.GetPrimitiveTypeReference(typeof(bool)), null, false /*isBindable*/); lotsofOrders.Function.AsEdmFunction().AddParameter("Person", personType.ToTypeReference()); metadata.AddFunctionAndFunctionImport("HowManyPotatoesEaten", null, MetadataUtils.GetPrimitiveTypeReference(typeof(Int32)), null, false); metadata.AddFunctionAndFunctionImport("QuoteOfTheDay", null, MetadataUtils.GetPrimitiveTypeReference(typeof(string)), null, false); // ACTIONS var action1 = metadata.AddAction("ChangeAddress", personType.ToTypeReference(), null /*returnType*/, true /*isbound*/, null /*entitySetPathExpression*/); action1.AddParameter(new EdmOperationParameter(action1, "Street", MetadataUtils.GetPrimitiveTypeReference(typeof(string)))); action1.AddParameter(new EdmOperationParameter(action1, "City", MetadataUtils.GetPrimitiveTypeReference(typeof(string)))); action1.AddParameter(new EdmOperationParameter(action1, "PostalCode", MetadataUtils.GetPrimitiveTypeReference(typeof(string)))); metadata.AddActionImport("ChangeAddress", action1, null /*entitySet*/); // Unbound action with no parameters var getRecentCustomersAction = metadata.AddAction("GetRecentCustomers", null /*boundType*/, new EdmCollectionTypeReference(new EdmCollectionType(orderType.ToTypeReference())), false /*isbound*/, null /*entitySetPathExpression*/); metadata.AddActionImport("GetRecentCustomers", getRecentCustomersAction, orderset); //Adding order details navigation property to order. var orderDetailNavigation = metadata.AddResourceSetReferenceProperty(orderType, "OrderDetails", orderdetailsSet, null); //Adding orders navigation to Customer. var ordersNavigation = metadata.AddResourceSetReferenceProperty(customerType, "Orders", orderset, null); ((EdmEntitySet)customerset).AddNavigationTarget(ordersNavigation, orderset); //Adding parent navigation to person metadata.AddResourceSetReferenceProperty(personType, "Parent", null, personType); //Since the people set can contain a customer we need to include the target for that navigation in the people set. ((EdmEntitySet)peopleset).AddNavigationTarget(ordersNavigation, orderset); //Since the OrderSet can contain a OrderDetail we need to include the target for that navigation in the order set. ((EdmEntitySet)orderset).AddNavigationTarget(orderDetailNavigation, orderdetailsSet); //Since the OrderDetailSet can contain a AssociatedOrder we need to include the target for that navigation in the orderdetail set. ((EdmEntitySet)orderdetailsSet).AddNavigationTarget(associatedOrderNavigation, orderset); //Since the OrderDetailSet can contain a ProductOrdered we need to include the target for that navigation in the orderdetail set. ((EdmEntitySet)orderdetailsSet).AddNavigationTarget(productOrderedNavigation, productset); ((EdmSingleton)specialOrder).AddNavigationTarget(orderDetailNavigation, orderdetailsSet); ((EdmSingleton)specialPerson).AddNavigationTarget(ordersNavigation, orderset); this.model = metadata; } return this.model; }
/// <summary> /// Adds a <paramref name="property"/> to <paramref name="metadata"/> /// under type <paramref name="structuredType"/> /// </summary> /// <param name="metadata">Metadata document to be added to</param> /// <param name="property">Property to add</param> /// <param name="structuredType">Structured type to add property to</param> private static void AddProperty(ConstructableMetadata metadata, EdmProperty property, IEdmStructuredType structuredType) { if (property.BuiltInTypeKind == BuiltInTypeKind.EdmProperty) { AddPrimitiveProperty(metadata, DataTypeToSystemType((PrimitiveType)property.TypeUsage.EdmType, IsNullable(property.TypeUsage)), property, structuredType); } else if (property.BuiltInTypeKind == BuiltInTypeKind.ComplexType) { metadata.AddComplexProperty(structuredType, property.Name, metadata.FindType(property.TypeUsage.EdmType.FullName) as IEdmComplexType); } }
/// <summary> /// Creates metadata properties for the specified enumeration of OData OM properties. /// </summary> /// <param name="model">The model to use.</param> /// <param name="owningType">The owning type to which to add the properties.</param> /// <param name="properties">Enumeration of the properties to add.</param> private static void CreateMetadataProperties(ConstructableMetadata model, EdmStructuredType owningType, IEnumerable<ODataProperty> properties) { if (properties == null) { return; } EdmEntityType owningEntityType = owningType as EdmEntityType; foreach (ODataProperty property in properties) { ODataPropertyMetadataAnnotation propertyMetadataAnnotation = property.GetInheritedAnnotation<ODataPropertyMetadataAnnotation>(); object propertyValue = property.Value; bool isOpenProperty = false; if (propertyMetadataAnnotation != null) { isOpenProperty = propertyMetadataAnnotation.IsOpenProperty; ExceptionUtilities.Assert( !isOpenProperty || owningType is EdmEntityType, "Can't declare an open property on non-entity type."); if (propertyMetadataAnnotation.PropertyValueForTypeInference != null) { propertyValue = propertyMetadataAnnotation.PropertyValueForTypeInference; } } ODataComplexValue complexValue = propertyValue as ODataComplexValue; if (complexValue != null) { ProcessComplexProperty(model, owningType, complexValue, isOpenProperty); } else { bool isKeyProperty = false; if (propertyMetadataAnnotation != null) { isKeyProperty = (propertyMetadataAnnotation.Kind & ODataPropertyKind.Key) == ODataPropertyKind.Key; bool isETagProperty = (propertyMetadataAnnotation.Kind & ODataPropertyKind.ETag) == ODataPropertyKind.ETag; ExceptionUtilities.Assert( !isKeyProperty || owningType is EdmEntityType, "Can't declare a key property on non-entity type."); ExceptionUtilities.Assert( !isETagProperty || owningType is EdmEntityType, "Can't declare an etag property on non-entity type."); ExceptionUtilities.Assert( !isOpenProperty || !(isKeyProperty || isETagProperty), "Key or etag property can't be open."); } if (owningEntityType != null && isOpenProperty) { //TODO: figure out how to set entity type to open after creation. } else { if (owningEntityType != null && isKeyProperty) { ExceptionUtilities.Assert(propertyValue != null, "Found a key property with null value, can't infer type from value in that case and it's invalid anyway."); var keyPropertyType = GetPrimitiveTypeReference(propertyValue.GetType()); var keyProperty = owningEntityType.AddStructuralProperty(property.Name, keyPropertyType); owningEntityType.AddKeys(keyProperty); } else { if (propertyValue is ODataCollectionValue) { ProcessCollectionProperty(model, owningType, propertyValue, owningEntityType, property); } else { Type propertyType = propertyValue == null ? typeof(string) : propertyValue.GetType(); owningType.AddProperty(new EdmStructuralProperty(owningType, property.Name, GetPrimitiveTypeReference(propertyType))); } } } } } }
/// <summary> /// Creates metadata for an entity derived from the data. /// </summary> /// <param name="model">The created model.</param> /// <param name="namespaceName">The namespace name for the new type(s).</param> /// <param name="entry">The entry to create the metadata for.</param> /// <returns>The entity type of the created entity.</returns> public static IEdmEntityType CreateEntityMetadata(string namespaceName, ODataEntry entry, out IEdmModel model) { var metadata = new ConstructableMetadata("DefaultContainer", namespaceName); ExceptionUtilities.Assert(entry.TypeName.StartsWith(namespaceName + "."), "The type name must start with Namespace."); EdmEntityType entityType = metadata.AddEntityType(entry.TypeName, null, null, false) as EdmEntityType; CreateMetadataProperties(metadata, entityType, entry.Properties); model = metadata; return entityType; }
private static void ProcessComplexProperty(ConstructableMetadata model, EdmStructuredType owningType, ODataComplexValue complexValue, bool isOpenProperty) { string namespaceName = ""; var schematype = owningType as IEdmSchemaType; if (schematype != null) { namespaceName = schematype.Namespace; } ExceptionUtilities.Assert(complexValue.TypeName.StartsWith(namespaceName + "."), "The type name must start with the same namespace as its owning type."); EdmComplexType complexType = model.FindType(complexValue.TypeName) as EdmComplexType; if (complexType == null) { string complexTypeLocalName = complexValue.GetUnqualifiedTypeName(namespaceName); complexType = model.AddComplexType(complexTypeLocalName, null, null, false) as EdmComplexType; CreateMetadataProperties(model, complexType, complexValue.Properties); } if (isOpenProperty) { //TODO: Find way to set parent type to open. } else { ExceptionUtilities.CheckObjectNotNull(complexType, "Complex type cannot be null"); owningType.AddProperty(new EdmStructuralProperty(owningType, complexType.Name, complexType.ToTypeReference())); } }
private static IEdmCollectionType GetCollectionType(ConstructableMetadata model, IEdmPrimitiveTypeReference primitiveItemType, string itemTypeName) { if (primitiveItemType != null) { return new EdmCollectionType(primitiveItemType); } IEdmType itemType = model.FindDeclaredType(itemTypeName); IEdmComplexType complexItemType = itemType as IEdmComplexType; if (complexItemType != null) { return new EdmCollectionType(complexItemType.ToTypeReference()); } return null; }
private static void ProcessCollectionProperty(ConstructableMetadata model, EdmStructuredType owningType, object propertyValue, EdmEntityType owningEntityType, ODataProperty property) { string itemTypeName = EdmModelUtils.GetCollectionItemTypeName(((ODataCollectionValue)propertyValue).TypeName); IEdmPrimitiveTypeReference primitiveItemType = MetadataUtils.GetPrimitiveTypeReference(itemTypeName); IEdmCollectionType collectionType = GetCollectionType(model, primitiveItemType, itemTypeName); ExceptionUtilities.Assert(collectionType != null, "Could not resolve item type."); owningType.AddProperty(new EdmStructuralProperty(owningType, property.Name, collectionType.ToTypeReference())); }