public void VerifyVersionOfEntityWithGeographyPropertyIsV3()
        {
            ResourceType rt = new ResourceType(typeof(IDictionary<string, string>), ResourceTypeKind.EntityType, null, "TestNamespace", "TestEntity", false);
            var keyProperty = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int))) {CanReflectOnInstanceTypeProperty = false };
            var geographyProperty = new ResourceProperty("GeographyProperty", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Geography))) { CanReflectOnInstanceTypeProperty = false };
            rt.AddProperty(keyProperty);
            rt.AddProperty(geographyProperty);
            rt.SetReadOnly();

            Assert.AreEqual(v4, rt.MetadataVersion, "MetadataVersion must be 4.0");
            Assert.AreEqual(MetadataEdmSchemaVersion.Version4Dot0, rt.SchemaVersion, "Schema version must be 4.0");
        }
        public OperationLinkBuilderTests()
        {
            ResourceType intType = ResourceType.GetPrimitiveResourceType(typeof(int));

            var customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "Customer", false);
            customerType.CanReflectOnInstanceType = false;
            customerType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, intType) { CanReflectOnInstanceTypeProperty = false });
            customerType.SetReadOnly();

            var operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", intType) }, null);
            operation.SetReadOnly();
            this.operationWithParameters = new OperationWrapper(operation);

            var typeWithEscapedName = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "FQ NS", "+ /", false);
            typeWithEscapedName.CanReflectOnInstanceType = false;
            typeWithEscapedName.AddProperty(new ResourceProperty("Number", ResourcePropertyKind.Primitive, intType) { CanReflectOnInstanceTypeProperty = false });
            typeWithEscapedName.SetReadOnly();

            operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", typeWithEscapedName) }, null);
            operation.SetReadOnly();
            this.operationWithEscapedParameter = new OperationWrapper(operation);

            var bestCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, customerType, "FQ.NS", "BestCustomer", false);
            bestCustomerType.SetReadOnly();

            operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType) }, null);
            operation.SetReadOnly();
            this.operationBoundToBaseType = new OperationWrapper(operation);

            this.entityToSerialize = EntityToSerialize.CreateFromExplicitValues(new object(), bestCustomerType, new TestSerializedEntityKey("http://odata.org/Service.svc/Customers/", bestCustomerType.FullName));

            var metadataUri = new Uri("http://odata.org/Service.svc/$metadata");
            this.testSubject = new OperationLinkBuilder("MyContainer", metadataUri);
        }
        public void DataServiceProviderWrapperShouldFailOnMultipleActionsWithSameNameAndBindingType()
        {
            var entityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Fake.NS", "Type", false) { CanReflectOnInstanceType = false };
            entityType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) {CanReflectOnInstanceTypeProperty = false});
            entityType.SetReadOnly();

            var resourceSet = new ResourceSet("MyEntitySet", entityType);
            resourceSet.SetReadOnly();

            ResourceType stringType = ResourceType.GetPrimitiveResourceType(typeof(string));
            var duplicateAction1 = new ServiceAction("Duplicate", stringType, OperationParameterBindingKind.Always, new[] { new ServiceActionParameter("p1", entityType), new ServiceActionParameter("p2", stringType) }, null);
            duplicateAction1.SetReadOnly();
            var duplicateAction2 = new ServiceAction("Duplicate", ResourceType.GetPrimitiveResourceType(typeof(int)), OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("p1", entityType) }, null);
            duplicateAction2.SetReadOnly();

            var actionProvider = new TestActionProvider
            {
                GetServiceActionsCallback = ctx => new[] { duplicateAction1, duplicateAction2 }
            };

            var providerWrapper = CreateProviderWrapper(actionProvider, p => p.AddResourceSet(resourceSet));

            Action getVisibleOperations = () => providerWrapper.GetVisibleOperations().ToList();
            getVisibleOperations.ShouldThrow<DataServiceException>()
                .WithMessage(ErrorStrings.DataServiceActionProviderWrapper_DuplicateAction("Duplicate"))
                .And.StatusCode.Should().Be(500);
        }
        /// <summary>
        /// Returns a test instance of one of the provider OM types.
        /// </summary>
        /// <param name="type">The type to get instance of.</param>
        /// <returns>The </returns>
        public static object GetTestInstance(Type type)
        {
            if (type == typeof(ResourceType))
            {
                ResourceProperty p = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false };
                ResourceType resourceType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "NoBaseType", false);
                Assert.IsTrue(resourceType.KeyProperties.Count == 0, "It's okay not to have key properties, since we haven't set this type to readonly yet");
                resourceType.AddProperty(p);
                return resourceType;
            }
            else if (type == typeof(ResourceProperty))
            {
                return new ResourceProperty("NonKeyProperty", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int)));
            }
            else if (type == typeof(ServiceOperation))
            {
                ServiceOperationParameter parameter = new ServiceOperationParameter("o", ResourceType.GetPrimitiveResourceType(typeof(string)));
                ServiceOperation operation = new ServiceOperation("Dummy", ServiceOperationResultKind.Void, null, null, "POST", new ServiceOperationParameter[] { parameter });
                return operation;
            }
            else if (type == typeof(ServiceOperationParameter))
            {
                return new ServiceOperationParameter("o", ResourceType.GetPrimitiveResourceType(typeof(string)));
            }
            else if (type == typeof(ResourceSet))
            {
                ResourceProperty p = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int)));

                ResourceType resourceType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "NoBaseType", false);
                resourceType.AddProperty(p);
                return new ResourceSet("Customers", resourceType);
            }

            throw new Exception(String.Format("Unexpected type encountered: '{0}'", type.FullName));
        }
        public OperationSerializerTests()
        {
            ResourceType intType = ResourceType.GetPrimitiveResourceType(typeof(int));

            var customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "Customer", false);
            customerType.CanReflectOnInstanceType = false;
            customerType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, intType) { CanReflectOnInstanceTypeProperty = false });
            customerType.SetReadOnly();

            var operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", intType) }, null);
            operation.SetReadOnly();
            this.baseTypeOperation = new OperationWrapper(operation);

            var bestCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, customerType, "FQ.NS", "BestCustomer", false);
            bestCustomerType.SetReadOnly();

            operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", bestCustomerType) }, null);
            operation.SetReadOnly();
            this.derivedTypeOperation = new OperationWrapper(operation);

            operation = new ServiceAction("Unambiguous", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType) }, null);
            operation.SetReadOnly();
            this.unambiguousOperation = new OperationWrapper(operation);

            this.entityToSerialize = EntityToSerialize.CreateFromExplicitValues(new object(), bestCustomerType, new TestSerializedEntityKey("http://odata.org/Service.svc/Customers(0)/", bestCustomerType.FullName));

            this.testSubject = CreateOperationSerializer(AlwaysAdvertiseActions);
        }
Exemple #6
0
        public void AddResourceProperty(string propertyName, string addToResourceType, string resourcePropertyKind,
                                        string propertyType, string containerName, bool isClrProperty)
        {
            DSP.ResourceProperty newProperty      = null;
            DSP.ResourceType     resourceType     = GetResourceType(addToResourceType);
            DSP.ResourceSet      assocResourceSet = GetResourceSet(containerName);

            if (assocResourceSet != null)
            {
                DSP.ResourceType assocResourceType = GetResourceType(propertyType);

                newProperty = new DSP.ResourceProperty(
                    propertyName,
                    ConvertResourcePropertyKind(resourcePropertyKind),
                    assocResourceType);
            }
            else
            {
                DSP.ResourceType assocResourceType = GetResourceType(propertyType);
                if (assocResourceType == null)
                {
                    Type t = Type.GetType(propertyType);
                    assocResourceType = DSP.ResourceType.GetPrimitiveResourceType(t);
                }

                newProperty = new DSP.ResourceProperty(
                    propertyName,
                    ConvertResourcePropertyKind(resourcePropertyKind),
                    assocResourceType);
            }

            newProperty.CanReflectOnInstanceTypeProperty = isClrProperty;
            resourceType.AddProperty(newProperty);
        }
        public void InvalidCasesTest()
        {
            ResourceProperty id = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false };
            ResourceType customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "CustomerType", false);
            customerType.AddProperty(id);
            ResourceType orderType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "OrderType", false);
            orderType.AddProperty(id);

            ResourceProperty customerOrders = new ResourceProperty("Orders", ResourcePropertyKind.ResourceSetReference, orderType);
            ResourceProperty orderCustomer = new ResourceProperty("Customer", ResourcePropertyKind.ResourceReference, customerType);
            customerType.AddProperty(customerOrders);
            orderType.AddProperty(orderCustomer);
            customerOrders.CanReflectOnInstanceTypeProperty = false;
            orderCustomer.CanReflectOnInstanceTypeProperty = false;
            customerType.SetReadOnly();
            orderType.SetReadOnly();

            ResourceSet customerSet = new ResourceSet("Customers", customerType);
            ResourceSet orderSet = new ResourceSet("Orders", orderType);

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSetEnd),
                "Value cannot be null.\r\nParameter name: resourceSet",
                null, customerType, customerOrders);

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSetEnd),
                "Value cannot be null.\r\nParameter name: resourceType",
                customerSet, null, customerOrders);

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSetEnd),
                "The resourceProperty parameter must be a navigation property on the resource type specified by the resourceType parameter.",
                customerSet, customerType, id);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSetEnd),
                "The resourceProperty parameter must be a navigation property on the resource type specified by the resourceType parameter.",
                customerSet, customerType, orderCustomer);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSetEnd),
                "The resourceType parameter must be a type that is assignable to the resource set specified by the resourceSet parameter.",
                customerSet,
                orderType,
                orderCustomer);
        }
        public void InvalidCasesTest()
        {
            ResourceProperty id = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false };
            ResourceType customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "CustomerType", false);
            customerType.AddProperty(id);
            ResourceType orderType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "OrderType", false);
            orderType.AddProperty(id);

            ResourceProperty customerOrders = new ResourceProperty("Orders", ResourcePropertyKind.ResourceSetReference, orderType);
            ResourceProperty orderCustomer = new ResourceProperty("Customer", ResourcePropertyKind.ResourceReference, customerType);
            customerType.AddProperty(customerOrders);
            orderType.AddProperty(orderCustomer);
            customerOrders.CanReflectOnInstanceTypeProperty = false;
            orderCustomer.CanReflectOnInstanceTypeProperty = false;
            customerType.SetReadOnly();
            orderType.SetReadOnly();

            ResourceSet customerSet = new ResourceSet("Customers", customerType);
            ResourceSet orderSet = new ResourceSet("Orders", orderType);

            ResourceAssociationSetEnd end1 = new ResourceAssociationSetEnd(customerSet, customerType, null);
            ResourceAssociationSetEnd end2 = new ResourceAssociationSetEnd(orderSet, orderType, null);

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSet), 
                "Value cannot be null or empty.\r\nParameter name: name", 
                null, end1, end2);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSet), 
                "Value cannot be null or empty.\r\nParameter name: name", 
                string.Empty, end1, end2);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSet), 
                "Value cannot be null.\r\nParameter name: end1", 
                "Customer_Order", null, end2);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSet), 
                "Value cannot be null.\r\nParameter name: end2", 
                "Customer_Order", end1, null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceAssociationSet),
                "The ResourceProperty of the ResourceAssociationEnds cannot both be null.", 
                "Customer_Order", end1, end2);

        }
        private static EntityToSerialize CreateEntityToSerialize(bool shouldIncludeTypeSegment)
        {
            ResourceType baseType = new ResourceType(typeof(MyType), ResourceTypeKind.EntityType, null, "TestNamespace", "BaseType", /*isAbstract*/ false);
            baseType.AddProperty(new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, new ResourceType(typeof(int), ResourceTypeKind.Primitive, null, "int")));

            baseType.SetReadOnly();

            Uri serviceUri = new Uri("http://dummy");

            KeySerializer keySerializer = KeySerializer.Create(UrlConvention.CreateWithExplicitValue(false));

            Func<ResourceProperty, object> getPropertyValue = p => "fakePropertyValue";
            return EntityToSerialize.Create(new MyType { ID = 42 }, baseType, "MySet", shouldIncludeTypeSegment, getPropertyValue, keySerializer, serviceUri);
        }
        public void Initialize()
        {
            this.host = new DataServiceHost2Simulator();

            var context = new DataServiceOperationContext(this.host);
            this.dataServiceSimulator = new DataServiceSimulator { OperationContext = context };

            var providerSimulator = new DataServiceProviderSimulator();

            DataServiceStaticConfiguration staticConfiguration = new DataServiceStaticConfiguration(this.dataServiceSimulator.Instance.GetType(), providerSimulator);
            IDataServiceProviderBehavior providerBehavior = DataServiceProviderBehavior.CustomDataServiceProviderBehavior;

            var resourceType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "SelectTestNamespace", "Fake", false) { CanReflectOnInstanceType = false, IsOpenType = true };
            resourceType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false });
            var resourceSet = new ResourceSet("FakeSet", resourceType);
            resourceSet.SetReadOnly();

            providerSimulator.AddResourceSet(resourceSet);

            var configuration = new DataServiceConfiguration(providerSimulator);
            configuration.SetEntitySetAccessRule("*", EntitySetRights.All);

            var provider = new DataServiceProviderWrapper(
                new DataServiceCacheItem(
                    configuration,
                    staticConfiguration), 
                providerSimulator, 
                providerSimulator, 
                this.dataServiceSimulator,
                false);

            this.dataServiceSimulator.ProcessingPipeline = new DataServiceProcessingPipeline();
            this.dataServiceSimulator.Provider = provider;
            provider.ProviderBehavior = providerBehavior;
            this.dataServiceSimulator.Configuration = new DataServiceConfiguration(providerSimulator);
            this.dataServiceSimulator.Configuration.DataServiceBehavior.MaxProtocolVersion = ODataProtocolVersion.V4;

            this.responseMessageSimulator = new ODataResponseMessageSimulator();
        }
        public void ResourceTypeMustBeDeclaringType_2()
        {
            ResourceType baseCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Test", "Employee", true /*isAbstract*/);
            ResourceType customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, baseCustomerType, "Test", "Customer", false /*isAbstract*/);
            ResourceSet customerSet = new ResourceSet("CustomerSet", customerType);

            ResourceType orderType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Test", "Employee", false /*isAbstract*/);
            ResourceProperty orderProperty = new ResourceProperty("Orders", ResourcePropertyKind.ResourceSetReference, orderType);
            baseCustomerType.AddProperty(orderProperty);

            try
            {
                new ResourceAssociationSetEnd(customerSet, customerType, orderProperty);
                Assert.Fail("Creating resource association set end should never have succeeded");
            }
            catch (ArgumentException e)
            {
                Assert.AreEqual(
                    DataServicesResourceUtil.GetString("ResourceAssociationSetEnd_ResourceTypeMustBeTheDeclaringType", customerType.FullName, orderProperty.Name),
                    e.Message);
            }
        }
        private static IDataServiceMetadataProvider PopulateMetadata(object dataSourceInstance)
        {
            List<ResourceType> types = new List<ResourceType>(4);
            ResourceType customer = new ResourceType(
                typeof(RowEntityTypeWithIDAsKey),
                ResourceTypeKind.EntityType,
                null, /*baseType*/
                "AstoriaUnitTests.Stubs", /*namespaceName*/
                "Customer",
                false /*isAbstract*/);

            customer.CanReflectOnInstanceType = false;

            ResourceType order = new ResourceType(
                typeof(RowEntityTypeWithIDAsKey),
                ResourceTypeKind.EntityType,
                null,
                "AstoriaUnitTests.Stubs",
                "Order",
                false);

            order.CanReflectOnInstanceType = false;

            ResourceType region = new ResourceType(
                typeof(RowEntityTypeWithIDAsKey),
                ResourceTypeKind.EntityType,
                null, /*baseType*/
                "AstoriaUnitTests.Stubs", /*namespaceName*/
                "Region",
                false /*isAbstract*/);

            region.CanReflectOnInstanceType = false;

            ResourceType address = new ResourceType(
                typeof(RowComplexType),
                ResourceTypeKind.ComplexType,
                null,
                "AstoriaUnitTests.Stubs",
                "Address",
                false);

            address.CanReflectOnInstanceType = false;

            ResourceType product = new ResourceType(
                typeof(RowEntityTypeWithIDAsKey),
                ResourceTypeKind.EntityType,
                null, /*baseType*/
                "AstoriaUnitTests.Stubs", /*namespaceName*/
                "Product",
                false /*isAbstract*/);

            product.CanReflectOnInstanceType = false;

            ResourceType orderDetail = new ResourceType(
                typeof(RowEntityType),
                ResourceTypeKind.EntityType,
                null, /*baseType*/
                "AstoriaUnitTests.Stubs", /*namespaceName*/
                "OrderDetail",
                false /*isAbstract*/);

            orderDetail.CanReflectOnInstanceType = false;

            ResourceType currency = new ResourceType(
                typeof(RowComplexType),
                ResourceTypeKind.ComplexType,
                null,
                "AstoriaUnitTests.Stubs",
                "CurrencyAmount",
                false);

            ResourceType headquarter = new ResourceType(
                typeof(RowComplexType),
                ResourceTypeKind.ComplexType,
                null,
                "AstoriaUnitTests.Stubs",
                "Headquarter",
                false);

            headquarter.CanReflectOnInstanceType = false;

            ResourceSet customerEntitySet = new ResourceSet("Customers", customer);
            ResourceSet orderEntitySet = new ResourceSet("Orders", order);
            ResourceSet regionEntitySet = new ResourceSet("Regions", region);
            ResourceSet productEntitySet = new ResourceSet("Products", product);
            ResourceSet orderDetailEntitySet = new ResourceSet("OrderDetails", orderDetail);

            ResourceSet memberCustomerEntitySet = new ResourceSet("MemberCustomers", customer);
            ResourceSet memberOrderEntitySet = new ResourceSet("MemberOrders", order);
            ResourceSet memberRegionEntitySet = new ResourceSet("MemberRegions", region);
            ResourceSet memberProductEntitySet = new ResourceSet("MemberProducts", product);
            ResourceSet memberOrderDetailEntitySet = new ResourceSet("MemberOrderDetails", orderDetail);

            ResourceProperty keyProperty = new ResourceProperty(
                "ID",
                ResourcePropertyKind.Key | ResourcePropertyKind.Primitive,
                ResourceType.GetPrimitiveResourceType(typeof(int)));

            // populate customer properties
            customer.AddProperty(keyProperty);
            customer.AddProperty(CreateNonClrProperty("Name", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(String))));
            customer.AddProperty(CreateNonClrProperty("BestFriend", ResourcePropertyKind.ResourceReference, customer));
            customer.AddProperty(CreateNonClrProperty("Orders", ResourcePropertyKind.ResourceSetReference, order));
            customer.AddProperty(CreateNonClrProperty("Region", ResourcePropertyKind.ResourceReference, region));
            customer.AddProperty(CreateNonClrProperty("Address", ResourcePropertyKind.ComplexType, address));
            customer.AddProperty(CreateNonClrProperty("GuidValue", ResourcePropertyKind.Primitive | ResourcePropertyKind.ETag, ResourceType.GetPrimitiveResourceType(typeof(Guid))));
            ResourceProperty property = CreateNonClrProperty("NameAsHtml", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)));
            property.MimeType = "text/html";
            customer.AddProperty(property);

            if (OpenWebDataServiceHelper.EnableBlobServer)
            {
                customer.IsMediaLinkEntry = true;
            }

            // create Customer With Birthday and populate its properties
            ResourceType customerWithBirthday = new ResourceType(
                typeof(RowEntityTypeWithIDAsKey),
                ResourceTypeKind.EntityType,
                customer, /*baseType*/
                "AstoriaUnitTests.Stubs", /*namespaceName*/
                "CustomerWithBirthday",
                false /*isAbstract*/);
            customerWithBirthday.AddProperty(CreateNonClrProperty("Birthday", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(DateTime))));

            customerWithBirthday.CanReflectOnInstanceType = false;

            // populate order properties
            order.AddProperty(keyProperty);
            order.AddProperty(CreateNonClrProperty("DollarAmount", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(double))));
            order.AddProperty(CreateNonClrProperty("OrderDetails", ResourcePropertyKind.ResourceSetReference, orderDetail));
            order.AddProperty(CreateNonClrProperty("CurrencyAmount", ResourcePropertyKind.ComplexType, currency));
            order.AddProperty(CreateNonClrProperty("Customer", ResourcePropertyKind.ResourceReference, customer));

            // populate region properties
            region.AddProperty(keyProperty);
            region.AddProperty(CreateNonClrProperty("Name", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))));
            region.AddProperty(CreateNonClrProperty("Headquarter", ResourcePropertyKind.ComplexType, headquarter));
            
            //populate headquarter properties
            headquarter.AddProperty(CreateNonClrProperty("DrivingDirections", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))));
            headquarter.AddProperty(CreateNonClrProperty("Address", ResourcePropertyKind.ComplexType, address));

            //populate address properties
            address.AddProperty(CreateNonClrProperty("StreetAddress", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))));
            address.AddProperty(CreateNonClrProperty("City", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))));
            address.AddProperty(CreateNonClrProperty("State", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))));
            address.AddProperty(CreateNonClrProperty("PostalCode", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))));

            // create product type and its properties
            product.AddProperty(keyProperty);
            product.AddProperty(CreateNonClrProperty("ProductName", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(String))));
            product.AddProperty(CreateNonClrProperty("Discontinued", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(bool))));
            product.AddProperty(CreateNonClrProperty("OrderDetails", ResourcePropertyKind.ResourceSetReference, orderDetail));

            // create order detail and its properties
            // DEVNOTE: it's very important that ProductID and OrderID are not in alphabetical order. There are tests relying on this.
            orderDetail.AddProperty(CreateNonClrProperty("ProductID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))));
            orderDetail.AddProperty(CreateNonClrProperty("OrderID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))));
            orderDetail.AddProperty(CreateNonClrProperty("UnitPrice", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(double))));
            orderDetail.AddProperty(CreateNonClrProperty("Quantity", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(short))));
            
            //populate currency amount properties
            currency.AddProperty(CreateNonClrProperty("Amount", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(decimal))));
            currency.AddProperty(CreateNonClrProperty("CurrencyName", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(String))));

            types.Add(customer);
            types.Add(customerWithBirthday);
            types.Add(order);
            types.Add(region);
            types.Add(address);
            types.Add(product);
            types.Add(orderDetail);
            types.Add(currency);
            types.Add(headquarter);

            List<ResourceSet> containers = new List<ResourceSet>(7);
            containers.Add(customerEntitySet);
            containers.Add(orderEntitySet);
            containers.Add(regionEntitySet);
            containers.Add(productEntitySet);
            containers.Add(orderDetailEntitySet);

            containers.Add(memberCustomerEntitySet);
            containers.Add(memberOrderEntitySet);
            containers.Add(memberRegionEntitySet);
            containers.Add(memberProductEntitySet);
            containers.Add(memberOrderDetailEntitySet);

            List<ServiceOperation> operations = new List<ServiceOperation>(1);
            operations.Add(new ServiceOperation("IntServiceOperation", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(int)), null, "GET", null));
            operations.Add(new ServiceOperation("InsertCustomer", ServiceOperationResultKind.DirectValue, customer, customerEntitySet, "POST",
                new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))),
                                                  new ServiceOperationParameter("name", ResourceType.GetPrimitiveResourceType(typeof(string))) }
                                                  ));
            operations.Add(new ServiceOperation("GetCustomerByCity", ServiceOperationResultKind.QueryWithMultipleResults, customer, customerEntitySet, "GET",
                new ServiceOperationParameter[] { new ServiceOperationParameter("city", ResourceType.GetPrimitiveResourceType(typeof(string))) }));
            operations.Add(new ServiceOperation("DoNothingOperation", ServiceOperationResultKind.Void, null, null, "POST", null));
            operations.Add(new ServiceOperation("GetCustomerAddress", ServiceOperationResultKind.DirectValue, address, null, "GET",
                new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))) }));
            operations.Add(new ServiceOperation("GetOrderById", ServiceOperationResultKind.DirectValue, order, orderEntitySet, "GET",
                new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))) }));

            operations.Add(new ServiceOperation("GetRegionByName", ServiceOperationResultKind.QueryWithMultipleResults, region, regionEntitySet, "GET",
                new ServiceOperationParameter[] { new ServiceOperationParameter("name", ResourceType.GetPrimitiveResourceType(typeof(string))) }));

            operations.Add(new ServiceOperation("AddressServiceOperation", ServiceOperationResultKind.DirectValue, address, null, "GET", null));

            operations.Add(new ServiceOperation("GetAllCustomersQueryable", ServiceOperationResultKind.QueryWithMultipleResults, customer, customerEntitySet, "GET", null));
            operations.Add(new ServiceOperation("GetCustomerByIdQueryable", ServiceOperationResultKind.QueryWithSingleResult, customer, customerEntitySet, "GET", new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))) }));
            operations.Add(new ServiceOperation("GetAllCustomersEnumerable", ServiceOperationResultKind.Enumeration, customer, customerEntitySet, "GET", null));
            operations.Add(new ServiceOperation("GetCustomerByIdDirectValue", ServiceOperationResultKind.DirectValue, customer, customerEntitySet, "GET", new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))) }));

            operations.Add(new ServiceOperation("GetAllOrdersQueryable", ServiceOperationResultKind.QueryWithMultipleResults, order, orderEntitySet, "GET", null));
            operations.Add(new ServiceOperation("GetOrderByIdQueryable", ServiceOperationResultKind.QueryWithSingleResult, order, orderEntitySet, "GET", new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))) }));
            operations.Add(new ServiceOperation("GetAllOrdersEnumerable", ServiceOperationResultKind.Enumeration, order, orderEntitySet, "GET", null));
            operations.Add(new ServiceOperation("GetOrderByIdDirectValue", ServiceOperationResultKind.DirectValue, order, orderEntitySet, "GET", new ServiceOperationParameter[] { new ServiceOperationParameter("id", ResourceType.GetPrimitiveResourceType(typeof(int))) }));

            List<ResourceAssociationSet> associationSets = new List<ResourceAssociationSet>();

            ResourceAssociationSet customer_BestFriend =
                new ResourceAssociationSet(
                    "Customers_BestFriend",
                    new ResourceAssociationSetEnd(customerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "BestFriend")),
                    new ResourceAssociationSetEnd(customerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "BestFriend")));
            associationSets.Add(customer_BestFriend);

            ResourceAssociationSet customer_Order =
                new ResourceAssociationSet(
                    "Customers_Orders",
                    new ResourceAssociationSetEnd(customerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Orders")),
                    new ResourceAssociationSetEnd(orderEntitySet, order, order.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Customer")));
            associationSets.Add(customer_Order);

            ResourceAssociationSet customer_Region =
                new ResourceAssociationSet(
                    "Customers_Regions",
                    new ResourceAssociationSetEnd(customerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Region")),
                    new ResourceAssociationSetEnd(regionEntitySet, region, region.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Customer")));
            associationSets.Add(customer_Region);

            ResourceAssociationSet order_OrderDetails =
                new ResourceAssociationSet(
                    "Orders_OrderDetails",
                    new ResourceAssociationSetEnd(orderEntitySet, order, order.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "OrderDetails")),
                    new ResourceAssociationSetEnd(orderDetailEntitySet, orderDetail, null));
            associationSets.Add(order_OrderDetails);

            ResourceAssociationSet product_OrderDetails =
                new ResourceAssociationSet(
                    "Products_OrderDetails",
                    new ResourceAssociationSetEnd(productEntitySet, product, product.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "OrderDetails")),
                    new ResourceAssociationSetEnd(orderDetailEntitySet, orderDetail, null));
            associationSets.Add(product_OrderDetails);

            ResourceAssociationSet memberCustomer_BestFriend =
                new ResourceAssociationSet(
                    "MemberCustomers_BestFriend",
                    new ResourceAssociationSetEnd(memberCustomerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "BestFriend")),
                    new ResourceAssociationSetEnd(memberCustomerEntitySet, customer, null));
            associationSets.Add(memberCustomer_BestFriend);

            ResourceAssociationSet memberCustomer_MemberOrder =
                new ResourceAssociationSet(
                    "MemberCustomers_MemberOrders",
                    new ResourceAssociationSetEnd(memberCustomerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Orders")),
                    new ResourceAssociationSetEnd(memberOrderEntitySet, order, order.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Customer")));
            associationSets.Add(memberCustomer_MemberOrder);

            ResourceAssociationSet memberCustomer_MemberRegion =
                new ResourceAssociationSet(
                    "MemberCustomers_MemberRegions",
                    new ResourceAssociationSetEnd(memberCustomerEntitySet, customer, customer.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Region")),
                    new ResourceAssociationSetEnd(memberRegionEntitySet, region, region.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "Customer")));
            associationSets.Add(memberCustomer_MemberRegion);

            ResourceAssociationSet memberOrder_MemberOrderDetails =
                new ResourceAssociationSet(
                    "MemberOrder_MemberOrderDetails",
                    new ResourceAssociationSetEnd(memberOrderEntitySet, order, order.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "OrderDetails")),
                    new ResourceAssociationSetEnd(memberOrderDetailEntitySet, orderDetail, null));
            associationSets.Add(memberOrder_MemberOrderDetails);

            ResourceAssociationSet memberProduct_MemberOrderDetails =
                new ResourceAssociationSet(
                    "MemberProduct_MemberOrderDetails",
                    new ResourceAssociationSetEnd(memberProductEntitySet, product, product.PropertiesDeclaredOnThisType.FirstOrDefault(p => p.Name == "OrderDetails")),
                    new ResourceAssociationSetEnd(memberOrderDetailEntitySet, orderDetail, null));
            associationSets.Add(memberProduct_MemberOrderDetails);

            return new CustomDataServiceProvider(containers, types, operations, associationSets, dataSourceInstance);
        }
        public void GetTargetSetTestsSetBindingTypeShouldPerformCorrectValidation()
        {
            ResourceType actorEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "foo", "Actor", false) { CanReflectOnInstanceType = false };
            ResourceProperty idProperty = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false };
            actorEntityType.AddProperty(idProperty);

            ResourceType addressComplexType = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "foo", "Address", false) { CanReflectOnInstanceType = false };
            addressComplexType.AddProperty(new ResourceProperty("StreetAddress", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))) { CanReflectOnInstanceTypeProperty = false });
            addressComplexType.AddProperty(new ResourceProperty("ZipCode", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false });

            actorEntityType.AddProperty(new ResourceProperty("PrimaryAddress", ResourcePropertyKind.ComplexType, addressComplexType) { CanReflectOnInstanceTypeProperty = false });
            actorEntityType.AddProperty(new ResourceProperty("OtherAddresses", ResourcePropertyKind.Collection, ResourceType.GetCollectionResourceType(addressComplexType)) { CanReflectOnInstanceTypeProperty = false });

            ResourceType movieEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "foo", "Movie", false) { CanReflectOnInstanceType = false };
            movieEntityType.AddProperty(idProperty);

            ResourceProperty moviesNavProp = new ResourceProperty("Movies", ResourcePropertyKind.ResourceSetReference, movieEntityType) { CanReflectOnInstanceTypeProperty = false };
            actorEntityType.AddProperty(moviesNavProp);
            ResourceProperty actorsNavProp = new ResourceProperty("Actors", ResourcePropertyKind.ResourceSetReference, actorEntityType) { CanReflectOnInstanceTypeProperty = false };
            movieEntityType.AddProperty(actorsNavProp);

            ResourceType derivedActorEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, actorEntityType, "foo", "DerivedActor", false) { CanReflectOnInstanceType = false };
            ResourceType derivedMovieEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, movieEntityType, "foo", "DerivedMovie", false) { CanReflectOnInstanceType = false };

            actorEntityType.SetReadOnly();
            derivedActorEntityType.SetReadOnly();
            movieEntityType.SetReadOnly();
            derivedMovieEntityType.SetReadOnly();
            addressComplexType.SetReadOnly();
            DataServiceProviderSimulator providerSimulator = new DataServiceProviderSimulator();
            providerSimulator.AddResourceType(actorEntityType);
            providerSimulator.AddResourceType(derivedActorEntityType);
            providerSimulator.AddResourceType(movieEntityType);
            providerSimulator.AddResourceType(derivedMovieEntityType);
            providerSimulator.AddResourceType(addressComplexType);

            ResourceSet actorSet = new ResourceSet("Actors", actorEntityType);
            ResourceSet movieSet = new ResourceSet("Movies", movieEntityType);
            actorSet.SetReadOnly();
            movieSet.SetReadOnly();
            providerSimulator.AddResourceSet(actorSet);
            providerSimulator.AddResourceSet(movieSet);

            providerSimulator.AddResourceAssociationSet(new ResourceAssociationSet("Actors_Movies", new ResourceAssociationSetEnd(actorSet, actorEntityType, moviesNavProp), new ResourceAssociationSetEnd(movieSet, movieEntityType, actorsNavProp)));

            DataServiceConfiguration config = new DataServiceConfiguration(providerSimulator);
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = ODataProtocolVersion.V4;

            var dataService = new DataServiceSimulator()
            {
                OperationContext = new DataServiceOperationContext(new DataServiceHostSimulator())
            };

            dataService.ProcessingPipeline = new DataServiceProcessingPipeline();
            DataServiceStaticConfiguration staticConfiguration = new DataServiceStaticConfiguration(dataService.Instance.GetType(), providerSimulator);
            IDataServiceProviderBehavior providerBehavior = DataServiceProviderBehavior.CustomDataServiceProviderBehavior;

            DataServiceProviderWrapper provider = new DataServiceProviderWrapper(
                new DataServiceCacheItem(
                    config, 
                    staticConfiguration), 
                providerSimulator, 
                providerSimulator, 
                dataService,
                false);
            dataService.Provider = provider;
            provider.ProviderBehavior = providerBehavior;

            var testCases = new[]
            {
                new
                {
                    AppendParameterName = true,
                    Path = "",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    ErrorMessage = default(string)
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Movies",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set),
                    ErrorMessage = default(string)
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Movies/Actors/Movies",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set),
                    ErrorMessage = default(string)
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/foo.DerivedActor/Movies/foo.DerivedMovie/Actors/foo.DerivedActor/Movies",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set),
                    ErrorMessage = default(string)
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/' is not a valid expression because it contains an empty segment or it ends with '/'."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Movies/",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/Movies/' is not a valid expression because it contains an empty segment or it ends with '/'."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Movies//Actors",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set),
                    ErrorMessage = "The path expression '{0}/Movies//Actors' is not a valid expression because it contains an empty segment or it ends with '/'."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/foo.DerivedActor",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/foo.DerivedActor' is not a valid expression because it ends with the type identifier 'foo.DerivedActor'. A valid path expression must not end in a type identifier."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Movies/foo.DerivedMovie",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/Movies/foo.DerivedMovie' is not a valid expression because it ends with the type identifier 'foo.DerivedMovie'. A valid path expression must not end in a type identifier."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Foo",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/Foo' is not a valid expression because the segment 'Foo' is not a type identifier or a property on the resource type 'foo.Actor'."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/ID",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/ID' is not a valid expression because the segment 'ID' is a property of type 'Edm.Int32'. A valid path expression must only contain properties of entity type."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/OtherAddresses",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/OtherAddresses' is not a valid expression because the segment 'OtherAddresses' is a property of type 'Collection(foo.Address)'. A valid path expression must only contain properties of entity type."
                },
                new
                {
                    AppendParameterName = true,
                    Path = "/Movies/Actors/PrimaryAddress",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression '{0}/Movies/Actors/PrimaryAddress' is not a valid expression because the segment 'PrimaryAddress' is a property of type 'foo.Address'. A valid path expression must only contain properties of entity type."
                },
                new
                {
                    AppendParameterName = false,
                    Path = "foo",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression 'foo' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'."
                },
                new
                {
                    AppendParameterName = false,
                    Path = "abc/pqr",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression 'abc/pqr' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'."
                },
                new
                {
                    AppendParameterName = false,
                    Path = "actorParameter",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression 'actorParameter' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'."
                },
                new
                {
                    AppendParameterName = false,
                    Path = "actorsParameter",
                    BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set),
                    TargetSet = default(ResourceSetWrapper),
                    ErrorMessage = "The path expression 'actorsParameter' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'."
                },
            };

            ServiceActionParameter actorParameter = new ServiceActionParameter("actor", actorEntityType);
            ServiceActionParameter actorsParameter = new ServiceActionParameter("actors", ResourceType.GetEntityCollectionResourceType(actorEntityType));
            var parameters = new ServiceActionParameter[] { actorParameter, actorsParameter };

            foreach (var parameter in parameters)
            {
                foreach (var testCase in testCases)
                {
                    string pathString = testCase.AppendParameterName ? parameter.Name + testCase.Path : testCase.Path;
                    var path = new ResourceSetPathExpression(pathString);
                    Assert.AreEqual(pathString, path.PathExpression);
                    string expectedErrorMessage = testCase.ErrorMessage == null ? null : string.Format(testCase.ErrorMessage, parameter.Name);
                    try
                    {
                        path.SetBindingParameter(parameter);
                        path.InitializePathSegments(provider);
                        var targetSet = path.GetTargetSet(provider, testCase.BindingSet);
                        Assert.IsNull(expectedErrorMessage, "Expecting exception but received none.");
                        Assert.AreEqual(targetSet.Name, testCase.TargetSet.Name);
                    }
                    catch (InvalidOperationException e)
                    {
                        Assert.AreEqual(expectedErrorMessage, e.Message);
                    }
                }
            }
        }
        public void AddingPropertiesTest()
        {
            ResourceType complexType = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "namespace", "Address", false);
            ResourceType entityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Namespace", "Order", false);

            IEnumerable<ResourceProperty> primitiveResourceProperties =
                ResourceTypeUtils.GetPrimitiveResourceTypes().Where(rt => rt.FullName != "Edm.Stream").Select(rt => new ResourceProperty("PrimitiveProperty", ResourcePropertyKind.Primitive, rt));
            IEnumerable<ResourceProperty> keyResourceProperties =
                GetKeyResourceTypes().Where(rt => !rt.FullName.StartsWith("Edm.Geography") && !rt.FullName.StartsWith("Edm.Geometry")).Select(rt => new ResourceProperty("KeyProperty", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, rt));
            IEnumerable<ResourceProperty> etagResourceProperties =
                ResourceTypeUtils.GetPrimitiveResourceTypes().Where(rt => !rt.FullName.StartsWith("Edm.Geography") && !rt.FullName.StartsWith("Edm.Geometry") && rt.FullName != "Edm.Stream").Select(rt => new ResourceProperty("ETagProperty", ResourcePropertyKind.Primitive | ResourcePropertyKind.ETag, rt));

            IEnumerable<ResourceProperty> complexProperties = new ResourceProperty[] {
                    new ResourceProperty("ComplexProperty", ResourcePropertyKind.ComplexType, complexType) };
            IEnumerable<ResourceProperty> navigationProperties = new ResourceProperty[] {
                    new ResourceProperty("ResourceReferenceProperty", ResourcePropertyKind.ResourceReference, entityType),
                    new ResourceProperty("ResourceSetReferenceProperty", ResourcePropertyKind.ResourceSetReference, entityType)};
            IEnumerable<ResourceProperty> collectionProperties = ResourceTypeUtils.GetPrimitiveResourceTypes().Except(new [] { ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)) }).Concat(new ResourceType[] { complexType })
                .Select(rt => new ResourceProperty("CollectionProperty", ResourcePropertyKind.Collection, ResourceType.GetCollectionResourceType(rt)));

            // Primitive resource type - no properties can be added
            foreach (ResourceType primitiveResourceType in ResourceTypeUtils.GetPrimitiveResourceTypes())
                foreach (ResourceProperty property in primitiveResourceProperties.Concat(keyResourceProperties).Concat(etagResourceProperties)
                                                        .Concat(complexProperties).Concat(navigationProperties).Concat(collectionProperties))
                {
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => primitiveResourceType.AddProperty(property),
                        string.Format("Adding property {0} to primitive resource type {1} should fail.", property.Name, primitiveResourceType.FullName));
                }

            // Collection resource type - no properties can be added
            foreach (ResourceType CollectionResourceType in ResourceTypeUtils.GetPrimitiveResourceTypes().Except(new[] { ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)) }).Concat(new ResourceType[] { complexType })
                                                            .Select(rt => ResourceType.GetCollectionResourceType(rt)))
                foreach (ResourceProperty property in primitiveResourceProperties.Concat(keyResourceProperties).Concat(etagResourceProperties)
                                                        .Concat(complexProperties).Concat(navigationProperties).Concat(collectionProperties))
                {
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => CollectionResourceType.AddProperty(property),
                        string.Format("Adding property {0} to a collection resource type {1} should fail.", property.Name, CollectionResourceType.FullName));
                }

            // Collection resource type - no properties can be added
            foreach (ResourceType collectionResourceType in (new ResourceType[] { entityType }).Select(rt => ResourceType.GetEntityCollectionResourceType(rt)))
                foreach (ResourceProperty property in primitiveResourceProperties.Concat(keyResourceProperties).Concat(etagResourceProperties)
                                                        .Concat(complexProperties).Concat(navigationProperties).Concat(collectionProperties))
                {
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => collectionResourceType.AddProperty(property),
                        string.Format("Adding property {0} to a collection resource type {1} should fail.", property.Name, collectionResourceType.FullName));
                }

            // Complex resource type - only primitive, complex and collection properties can be added
            foreach (ResourceProperty property in primitiveResourceProperties.Concat(complexProperties).Concat(collectionProperties))
            {
                ResourceType t = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "Ns", "Address", false);
                t.AddProperty(property);
            }

            // It is possible to add navigation property to a complex type.
            // Once this is fixed remove this foreach and add navigationProperties to the list for the next foreach.
            foreach (var p in navigationProperties)
            {
                ResourceType t = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "Ns", "Address", false);
                t.AddProperty(p);
                t.SetReadOnly();
            }

            foreach (ResourceProperty property in keyResourceProperties.Concat(etagResourceProperties))
            {
                ResourceType t = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "Ns", "Address", false);
                ExceptionUtils.ThrowsException<InvalidOperationException>(
                    () => t.AddProperty(property),
                    string.Format("Adding key or etag property {0} to complex resource type should fail.", property.Name));
            }

            // Entity resource type
            foreach (ResourceProperty property in primitiveResourceProperties.Concat(keyResourceProperties).Concat(etagResourceProperties)
                                                    .Concat(complexProperties).Concat(navigationProperties).Concat(collectionProperties))
            {
                ResourceType t = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Ns", "Order", false);
                if (property.ResourceType.InstanceType == typeof(System.IO.Stream) && (property.Kind & ResourcePropertyKind.Stream) != ResourcePropertyKind.Stream)
                {
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () =>
                        {
                            t.AddProperty(property);
                        },
                        "Adding Stream type as primitive property should fail");
                }
                else
                {
                    t.AddProperty(property);
                }
            }
        }
        private static ResourceType CreateResourceTypeWithKeyProperties(params string[] keyPropertyNames)
        {
            var resourceType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "EntityType", false) {CanReflectOnInstanceType = false};

            foreach (var keyPropertyName in keyPropertyNames)
            {
                resourceType.AddProperty(new ResourceProperty(keyPropertyName, ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) {CanReflectOnInstanceTypeProperty = false});
            }

            resourceType.SetReadOnly();
            return resourceType;
        }
        public void AddNamedStreamTest()
        {
            ResourceType type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.CanReflectOnInstanceType = false;
            ResourceProperty id = new ResourceProperty("ID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int)));
            type1.AddProperty(id);
            id.CanReflectOnInstanceTypeProperty = false;

            ResourceType type2 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, type1, "namespace", "type2", false);
            type2.AddProperty(new ResourceProperty("Stream1", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream))));
            type2.AddProperty(new ResourceProperty("Stream2", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream))));

            ResourceType type3 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, type2, "namespace", "type3", false);
            type3.AddProperty(new ResourceProperty("Stream3", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream))));

            Assert.AreEqual(0, type1.GetNamedStreams().Count(), "Wrong count");
            Assert.AreEqual(0, type1.GetNamedStreamsDeclaredOnThisType().Count(), "Wrong count");
            
            Assert.AreEqual(2, type2.GetNamedStreams().Count(), "Wrong count");
            Assert.IsNotNull(type2.GetNamedStreams().Single(s => s.Name == "Stream1"), "Unexpected null");
            Assert.IsNotNull(type2.GetNamedStreams().Single(s => s.Name == "Stream2"), "Unexpected null");
            
            Assert.AreEqual(2, type2.GetNamedStreamsDeclaredOnThisType().Count(), "Wrong count");
            Assert.IsNotNull(type2.GetNamedStreamsDeclaredOnThisType().Single(s => s.Name == "Stream1"), "Unexpected null");
            Assert.IsNotNull(type2.GetNamedStreamsDeclaredOnThisType().Single(s => s.Name == "Stream2"), "Unexpected null");
            
            Assert.AreEqual(3, type3.GetNamedStreams().Count(), "Wrong count");
            Assert.IsNotNull(type3.GetNamedStreams().Single(s => s.Name == "Stream1"), "Unexpected null");
            Assert.IsNotNull(type3.GetNamedStreams().Single(s => s.Name == "Stream2"), "Unexpected null");
            Assert.IsNotNull(type3.GetNamedStreams().Single(s => s.Name == "Stream3"), "Unexpected null");

            Assert.AreEqual(1, type3.GetNamedStreamsDeclaredOnThisType().Count(), "Wrong count");
            Assert.IsNotNull(type3.GetNamedStreamsDeclaredOnThisType().Single(s => s.Name == "Stream3"), "Unexpected null");

            type3.SetReadOnly();

            Assert.AreEqual(0, type1.GetNamedStreams().Count(), "Wrong count");
            Assert.AreEqual(0, type1.GetNamedStreamsDeclaredOnThisType().Count(), "Wrong count");

            Assert.AreEqual(2, type2.GetNamedStreams().Count(), "Wrong count");
            Assert.IsNotNull(type2.GetNamedStreams().Single(s => s.Name == "Stream1"), "Unexpected null");
            Assert.IsNotNull(type2.GetNamedStreams().Single(s => s.Name == "Stream2"), "Unexpected null");

            Assert.AreEqual(2, type2.GetNamedStreamsDeclaredOnThisType().Count(), "Wrong count");
            Assert.IsNotNull(type2.GetNamedStreamsDeclaredOnThisType().Single(s => s.Name == "Stream1"), "Unexpected null");
            Assert.IsNotNull(type2.GetNamedStreamsDeclaredOnThisType().Single(s => s.Name == "Stream2"), "Unexpected null");

            Assert.AreEqual(3, type3.GetNamedStreams().Count(), "Wrong count");
            Assert.IsNotNull(type3.GetNamedStreams().Single(s => s.Name == "Stream1"), "Unexpected null");
            Assert.IsNotNull(type3.GetNamedStreams().Single(s => s.Name == "Stream2"), "Unexpected null");
            Assert.IsNotNull(type3.GetNamedStreams().Single(s => s.Name == "Stream3"), "Unexpected null");

            Assert.AreEqual(1, type3.GetNamedStreamsDeclaredOnThisType().Count(), "Wrong count");
            Assert.IsNotNull(type3.GetNamedStreamsDeclaredOnThisType().Single(s => s.Name == "Stream3"), "Unexpected null");
        }
        public void AddNamedStreamValidationTest()
        {
            ResourceProperty idProperty = new ResourceProperty("ID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int)));
            ResourceProperty name = new ResourceProperty("Name", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)));
            idProperty.CanReflectOnInstanceTypeProperty = false;
            name.CanReflectOnInstanceTypeProperty = false;

            ResourceType entityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "entityType", false);

            // Adding named stream to readonly type
            entityType.AddProperty(idProperty);
            entityType.SetReadOnly();
            ResourceProperty stream1 = new ResourceProperty("Stream1", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)));
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => entityType.AddProperty(stream1),
                string.Format("The resource type '{0}' cannot be modified since it is already set to read-only.", entityType.FullName),
                "Adding to a sealed type should fail.");

            // Adding a named stream which will collide with properties declared on this type
            ResourceType type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.AddProperty(idProperty);
            ResourceProperty idStream = new ResourceProperty("ID", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)));
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type1.AddProperty(idStream),
                "A property with same name 'ID' already exists in type 'namespace.type1'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Named stream can't have the same name as a property.");

            // Adding a named stream which will collide with properties declared on base type
            type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.AddProperty(idProperty);
            ResourceType type2 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, type1, "namespace", "type2", false);
            // We don't detect name collision between named streams and properties until we enumerate properties after the type is set to readonly.
            idStream = new ResourceProperty("ID", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)));
            type2.AddProperty(idStream);
            type2.Properties.Count();
            type2.SetReadOnly();
            // Detect name collision now.
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type2.Properties.Count(),
                "A property with same name 'ID' already exists in type 'namespace.type2'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Named stream can't have the same name as a property on base type.");

            // Adding a named stream which will collide with named streams declared on this type
            type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.AddProperty(idProperty);
            ResourceProperty nameStream = new ResourceProperty("Name", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)));
            type1.AddProperty(nameStream);
            // Adding the same named stream instance
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type1.AddProperty(nameStream),
                "A property with same name 'Name' already exists in type 'namespace.type1'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Named stream can't have the same name as another named stream.");
            // Adding a new instance with the same name
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type1.AddProperty(new ResourceProperty("Name", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)))),
                "A property with same name 'Name' already exists in type 'namespace.type1'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Named stream can't have the same name as another named stream - new instance.");

            // Adding a named stream which will collide with named streams declared on base type
            type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.AddProperty(idProperty);
            type1.AddProperty(nameStream);
            type2 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, type1, "namespace", "type2", false);
            type2.AddProperty(nameStream);  // Adding the same instance
            type2.SetReadOnly();
            // Delay detection until ValidateType(), this is because you can add named streams to the base type that collides with those on the derived type.
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type2.Properties.Count(),
                "A property with same name 'Name' already exists in type 'namespace.type2'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Named stream can't have the same name as another named stream - new instance.");
            type2 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, type1, "namespace", "type2", false);
            type2.AddProperty(new ResourceProperty(nameStream.Name, ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream))));  // Adding a new instance with the same name
            type2.SetReadOnly();
            // Delay detection until ValidaType(), this is because you can add named streams to the base type that collides with those on the derived type.
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type2.Properties.Count(),
                "A property with same name 'Name' already exists in type 'namespace.type2'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Named stream can't have the same name as another named stream - new instance.");

            // Adding a property which will collide with named streams declared on this type
            type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.AddProperty(idProperty);
            nameStream = new ResourceProperty("Name", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)));
            type1.AddProperty(nameStream);
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type1.AddProperty(name),
                "A property with same name 'Name' already exists in type 'namespace.type1'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Can't add property with the same name as a named stream.");

            // Adding a property which will collide with named streams declared on base type
            type1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "namespace", "type1", true);
            type1.AddProperty(idProperty);
            type1.AddProperty(nameStream);
            // We don't detect name collision between named streams and properties until we enumerate properties after the type is set to readonly.
            type2 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, type1, "namespace", "type2", false);
            type2.AddProperty(name);
            type2.Properties.Count();
            type2.SetReadOnly();
            // Detect name collision now.
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => type2.Properties.Count(),
                "A property with same name 'Name' already exists in type 'namespace.type2'. Please make sure that there is no property with the same name defined in one of the base types.",
                "Can't add property with the same name as a named stream on base type.");
        }
        public void InvalidCasesTest()
        {
            ResourceType rt = (ResourceType)ResourceTypeUtils.GetTestInstance(typeof(ResourceType));

            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => rt.AddProperty(new ResourceProperty("ID", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(double)))),
                "A property with same name 'ID' already exists in type 'Foo.NoBaseType'. Please make sure that there is no property with the same name defined in one of the base types.",
                "cannot add property with the same name");

            ResourceType derivedType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, rt, "Namespace", "CustomerWithBirthday", false);

            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => derivedType.AddProperty(new ResourceProperty("p1", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int)))),
                "Key properties cannot be defined in derived types.",
                "Cannot add key properties to derived type");

            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => {
                    ResourceType resourceType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "NoBaseType", false);
                    resourceType.SetReadOnly();
                    // Accessing any properties collection will invoke the validation logic
                    resourceType.KeyProperties.Single();
                },
                "The entity type 'Foo.NoBaseType' does not have any key properties. Please make sure that one or more key properties are defined for this entity type.",
                "Entity Type must have key Properties");

            // should be able to add property with same name as that in the base type. This will throw when we make the derived type readonly.
            derivedType.AddProperty(new ResourceProperty("ID", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))));
            ExceptionUtils.ExpectedException<InvalidOperationException>(
                () => {
                    derivedType.SetReadOnly();
                    // Accessing any properties collection will invoke the validation logic
                    derivedType.Properties.First();
                },
                "A property with same name 'ID' already exists in type 'Namespace.CustomerWithBirthday'. Please make sure that there is no property with the same name defined in one of the base types.",
                "cannot add property with the same name as that in base type");

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceType),
                "ResourceTypeKind.Primitive, ResourceTypeKind.Collection and ResourceTypeKind.EntityCollection are not valid values for the 'resourceTypeKind' parameter.\r\nParameter name: resourceTypeKind",
                typeof(object), ResourceTypeKind.Primitive, null, "foo", "bar", false);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceType),
                "ResourceTypeKind.Primitive, ResourceTypeKind.Collection and ResourceTypeKind.EntityCollection are not valid values for the 'resourceTypeKind' parameter.\r\nParameter name: resourceTypeKind",
                typeof(object), ResourceTypeKind.Collection, null, "foo", "bar", false);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceType),
                "ResourceTypeKind.Primitive, ResourceTypeKind.Collection and ResourceTypeKind.EntityCollection are not valid values for the 'resourceTypeKind' parameter.\r\nParameter name: resourceTypeKind",
                typeof(object), ResourceTypeKind.EntityCollection, null, "foo", "bar", false);

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ResourceType),
                "The CLR type for the resource type cannot be a value type.\r\nParameter name: instanceType", 
                typeof(int), ResourceTypeKind.ComplexType, null, "foo", "bar", false);

            ExceptionUtils.ExpectedException<ArgumentException>(
                () => ResourceType.GetCollectionResourceType(ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream))),
                "The ItemType of a collection resource type cannot be of type 'Edm.Stream'.\r\nParameter name: itemType");

            ResourceType ct = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "Foo", "Bar", false);
            ResourceType pt = ResourceType.GetPrimitiveResourceType(typeof(string));
            ResourceType primitiveCollection = ResourceType.GetCollectionResourceType(pt);
            ResourceType et = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "EntityResourceType", false);
            ResourceType entityCollection = ResourceType.GetEntityCollectionResourceType(et);

            ExceptionUtils.ExpectedException<ArgumentException>(
                () => ResourceType.GetEntityCollectionResourceType(null),
                "Value cannot be null.\r\nParameter name: itemType");
            ResourceType[] types = new ResourceType[] { ct, pt, primitiveCollection, entityCollection };
            AstoriaTestNS.TestUtil.RunCombinations(
                types,
                r =>
                {
                    ExceptionUtils.ExpectedException<ArgumentException>(
                        () => ResourceType.GetEntityCollectionResourceType(r),
                        "Only collections of an entity type are supported.");
                });

            types = new ResourceType[] { ct, pt, primitiveCollection, entityCollection };
            ResourceProperty key = new ResourceProperty("KeyProperty", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)));
            ResourceProperty etag = new ResourceProperty("ETagProperty", ResourcePropertyKind.ETag | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)));
            ResourceProperty primitive = new ResourceProperty("Property", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)));
            AstoriaTestNS.TestUtil.RunCombinations(
                types,
                r =>
                {
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => r.AddProperty(key),
                        "Cannot add key property to non-entity types");

                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => r.AddProperty(etag),
                        "Cannot add key property to non-entity types");

                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => r.AddProperty(new ResourceProperty("Stream1", ResourcePropertyKind.Stream, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)))),
                        "Cannot add key property to non-entity types");
                });

            ExceptionUtils.ThrowsException<InvalidOperationException>(
                () =>
                {
                    pt.AddProperty(primitive);
                    primitiveCollection.AddProperty(primitive);
                    entityCollection.AddProperty(primitive);
                },
                "Cannot add property to primitive, primitive collection or entity collection types");

            types = new ResourceType[] { pt, ct, et, primitiveCollection, entityCollection };
            AstoriaTestNS.TestUtil.RunCombinations(
                new ResourceTypeKind[] { ResourceTypeKind.EntityType, ResourceTypeKind.ComplexType },
                types,
                (derivedTypeKind, baseResourceType) =>
                {
                    if (baseResourceType.ResourceTypeKind == derivedTypeKind)
                    {
                        return;
                    }

                    ExceptionUtils.ExpectedException<ArgumentException>(
                        () => new ResourceType(typeof(object), derivedTypeKind, baseResourceType, "Foo", "NewDerivedType", false),
                        string.Format("A resource type of kind '{0}' cannot derive from a base resource type of kind '{1}'. Inheritance is only supported when resource types are of the same kind.\r\nParameter name: resourceTypeKind",
                            derivedTypeKind.ToString(), baseResourceType.ResourceTypeKind.ToString()),
                        "Cannot derive from a resource type of different kind.");
                });
        }
        public void PropertyOrderValidationTest()
        {
            ResourceType rt = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, null, "baseType", false);
            ResourceType complexType = new ResourceType(typeof(string), ResourceTypeKind.ComplexType, null, "foo", "bar", false);
            ResourceType rt1 = new ResourceType(typeof(byte[]), ResourceTypeKind.EntityType, null, "foo", "derivedType", false);

            List<ResourceProperty> properties = new List<ResourceProperty>();
            ResourceProperty p = new ResourceProperty("p", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Single)));
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("k1", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int)));
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("k2", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(double)));
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("e1", ResourcePropertyKind.Primitive | ResourcePropertyKind.ETag, ResourceType.GetPrimitiveResourceType(typeof(DateTime)));
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("e2", ResourcePropertyKind.Primitive | ResourcePropertyKind.ETag, ResourceType.GetPrimitiveResourceType(typeof(string)));
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("complexprop", ResourcePropertyKind.ComplexType, complexType);
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("refProp", ResourcePropertyKind.ResourceReference, rt1);
            rt.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("collectionProp", ResourcePropertyKind.ResourceSetReference, rt1);
            rt.AddProperty(p);
            properties.Add(p);

            this.CheckOrdering(rt, properties, false);
            this.CheckOrdering(rt, properties, true);

            // now if the derived type add some more properties, then also ordering must be preserved
            ResourceType derivedType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, rt, "foo", "derived", false);
            p = new ResourceProperty("dp1", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(byte[])));
            derivedType.AddProperty(p);
            properties.Add(p);

            p = new ResourceProperty("detag", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Int64)));
            derivedType.AddProperty(p);
            properties.Add(p);

            this.CheckOrdering(derivedType, properties, false);

            Assert.IsTrue(derivedType.PropertiesDeclaredOnThisType[0] == properties[properties.Count - 2], "order of properties must be preserved");
            Assert.IsTrue(derivedType.PropertiesDeclaredOnThisType[1] == properties[properties.Count - 1], "order of properties must be preserved");
        }
        public void ServiceOperationConstructorInvalidTests()
        {
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "The 'resultType' parameter must be null when the 'resultKind' parameter value is 'Void', however the 'resultType' parameter cannot be null when the 'resultKind' parameter is of any value other than 'Void'. Please make sure that the 'resultKind' parameter value is set according to the 'resultType' parameter value.",
                "op", ServiceOperationResultKind.Void, ResourceType.GetPrimitiveResourceType(typeof(int)), null, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "The 'resultType' parameter must be null when the 'resultKind' parameter value is 'Void', however the 'resultType' parameter cannot be null when the 'resultKind' parameter is of any value other than 'Void'. Please make sure that the 'resultKind' parameter value is set according to the 'resultType' parameter value.",
                "op", ServiceOperationResultKind.DirectValue, null, null, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "A parameter with the name 'p1' already exists. Please make sure that every parameter has a unique name.",
                "op", ServiceOperationResultKind.Void, null, null, "GET",
                new ServiceOperationParameter[] { 
                        new ServiceOperationParameter("p1", ResourceType.GetPrimitiveResourceType(typeof(int))),
                        new ServiceOperationParameter("p1", ResourceType.GetPrimitiveResourceType(typeof(string))) });
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "The resource type 'Collection(foo.Customer)' is not a type that can be returned by a service operation. A service operation can only return values of an entity type, a complex type or any primitive type, other than the stream type.",
                "op", ServiceOperationResultKind.QueryWithMultipleResults, ResourceType.GetEntityCollectionResourceType(new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "foo", "Customer", false)), null, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "The resource type 'Edm.Stream' is not a type that can be returned by a service operation. A service operation can only return values of an entity type, a complex type or any primitive type, other than the stream type.",
                "op", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)), null, "GET", null);

            ResourceProperty p = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int)));
            ResourceType customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "NoBaseType", false);
            customerType.AddProperty(p);
            customerType.SetReadOnly();
            ResourceSet customerSet = new ResourceSet("Customers", customerType);
            ResourceType orderType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "NoBaseType", false);
            orderType.AddProperty(p);
            orderType.SetReadOnly();

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "'resultSet' must be null when 'resultType' is null or not an EntityType.",
                "op", ServiceOperationResultKind.Void, null, customerSet, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "'resultSet' must be null when 'resultType' is null or not an EntityType.",
                "op", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(int)), customerSet, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "When 'resultType' is an entity type, 'resultSet' cannot be null and the resource type of 'resultSet' must be assignable from 'resultType'.",
                "op", ServiceOperationResultKind.QueryWithMultipleResults, customerType, null, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "When 'resultType' is an entity type, 'resultSet' cannot be null and the resource type of 'resultSet' must be assignable from 'resultType'.",
                "op", ServiceOperationResultKind.QueryWithMultipleResults, orderType, customerSet, "GET", null);

            ExceptionUtils.ThrowsException<InvalidOperationException>(
                () =>
                {
                    ServiceOperation op = new ServiceOperation("op", ServiceOperationResultKind.Void, null, null, "GET", null);
                    op.MimeType = null;
                },
                "MimeType cannot be set to null");

            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "Value cannot be null or empty.\r\nParameter name: method",
                "op", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(int)), null, null, null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "Value cannot be null or empty.\r\nParameter name: method",
                "op", ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(int)), null, string.Empty, null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "Value cannot be null or empty.\r\nParameter name: name",
                null, ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(int)), null, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "Value cannot be null or empty.\r\nParameter name: name",
                string.Empty, ServiceOperationResultKind.DirectValue, ResourceType.GetPrimitiveResourceType(typeof(int)), null, "GET", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "An invalid HTTP method 'PUT' was specified for the service operation 'op'. Only the HTTP 'POST' and 'GET' methods are supported for service operations.",
                "op", ServiceOperationResultKind.Void, null, null, "PUT", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "An invalid HTTP method 'PATCH' was specified for the service operation 'op'. Only the HTTP 'POST' and 'GET' methods are supported for service operations.",
                "op", ServiceOperationResultKind.Void, null, null, "PATCH", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "An invalid HTTP method 'DELETE' was specified for the service operation 'op'. Only the HTTP 'POST' and 'GET' methods are supported for service operations.",
                "op", ServiceOperationResultKind.Void, null, null, "DELETE", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "An invalid HTTP method 'HEAD' was specified for the service operation 'op'. Only the HTTP 'POST' and 'GET' methods are supported for service operations.",
                "op", ServiceOperationResultKind.Void, null, null, "HEAD", null);
            ExceptionUtils.CheckInvalidConstructorParameters(
                typeof(ServiceOperation),
                "An invalid HTTP method 'Some Method' was specified for the service operation 'op'. Only the HTTP 'POST' and 'GET' methods are supported for service operations.",
                "op", ServiceOperationResultKind.Void, null, null, "Some Method", null);
        }
        private MessageWriterBuilder ForNormalRequest()
        {
            var requestDescription = new RequestDescription(RequestTargetKind.Resource, RequestTargetSource.EntitySet, new Uri("http://temp.org/"));

            var resourceType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Fake", "Type", false) { CanReflectOnInstanceType = false, IsOpenType = true };
            resourceType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false });
            var resourceSet = new ResourceSet("FakeSet", resourceType);
            resourceSet.SetReadOnly();

            requestDescription.LastSegmentInfo.TargetResourceType = resourceType;
            requestDescription.LastSegmentInfo.TargetResourceSet = ResourceSetWrapper.CreateForTests(resourceSet);

            return this.ForNormalRequest(requestDescription);
        }
        /// <summary>
        /// Add stream properties that are marked NamedStream in clrType to resourceType.
        /// </summary>
        /// <param name="resourceType">the given resource type.</param>
        /// <param name="clrType">backing clr type for the resource.</param>
        /// <param name="inherit">indicates if the resource type has a base type.</param>
        protected static void AddStreamProperties(ResourceType resourceType, Type clrType, bool inherit)
        {
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(clrType != null, "clrType != null");

            // Add named streams if there is any.
            // Note Named streams are like virtual properties and each ResourceType will inherit named streams from its parent type.
            // That's why we set inherit to 'false' when the base entity type is not null or else we get name collisions.
            // However if the NamedStreamAttribute is on a base type that is not an entity type, we want to inherit it by setting
            // inherit to 'true'.
            var namedStreamAttributes = clrType.GetCustomAttributes(typeof(NamedStreamAttribute), inherit).Cast<NamedStreamAttribute>().OrderBy(a => a.Name);
            foreach (var namedStream in namedStreamAttributes)
            {
                resourceType.AddProperty(new ResourceProperty((namedStream).Name, ResourcePropertyKind.Stream, PrimitiveResourceTypeMap.TypeMap.GetPrimitive(typeof(Stream))));
            }
        }
        public void CollectionTypeValidation()
        {
            ResourceType entityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "foo", "Order", false);
            ResourceType complexType = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "foo", "bar", false);
            ResourceType complexType2 = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "foo", "bar2", false);
            complexType2.AddProperty(new ResourceProperty("CollectionProperty", ResourcePropertyKind.Collection, ResourceType.GetCollectionResourceType(complexType)));

            var itemTypes = new ResourceType[] { complexType, complexType2 }.Concat(ResourceTypeUtils.GetPrimitiveResourceTypes());
            var collectionTypes = itemTypes.Except(new[] { ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)) }).Select(it => new { ItemType = it, CollectionType = ResourceType.GetCollectionResourceType(it) });
            AstoriaTestNS.TestUtil.RunCombinations(
                collectionTypes,
                c =>
                {
                    Assert.AreEqual(c.ItemType, c.CollectionType.ItemType, "The item type of the collection doesn't match the one specified upon creation.");
                    Assert.AreEqual("Collection(" + c.ItemType.FullName + ")", c.CollectionType.FullName, "The full name of a collection type is wrong.");
                    Assert.AreEqual("Collection(" + c.ItemType.FullName + ")", c.CollectionType.Name, "The name of a collection type is wrong.");
                    Assert.AreEqual("", c.CollectionType.Namespace, "The namespace of a collection type should be empty.");
                    Assert.IsTrue(c.CollectionType.IsReadOnly, "The collection type is always read-only.");
                    Assert.IsFalse(c.CollectionType.IsAbstract, "Collection type is never abstract.");
                    Assert.IsFalse(c.CollectionType.IsOpenType, "Collection type is never open.");
                    Assert.IsFalse(c.CollectionType.IsMediaLinkEntry, "Collection type is never an MLE.");
                    Assert.AreEqual(typeof(IEnumerable<>).MakeGenericType(c.ItemType.InstanceType), c.CollectionType.InstanceType, "The instance type of the collection type is wrong.");
                    Assert.IsTrue(c.CollectionType.CanReflectOnInstanceType, "Collection type has CanReflectOnInstanceType always true.");
                    Assert.IsNull(c.CollectionType.BaseType, "Collection type has never a base type.");
                    Assert.AreEqual(ResourceTypeKind.Collection, c.CollectionType.ResourceTypeKind, "The kind of a collection type is always Collection.");
                    Assert.AreEqual(0, c.CollectionType.PropertiesDeclaredOnThisType.Count(), "Collection type has no properties.");
                    Assert.AreEqual(0, c.CollectionType.Properties.Count(), "Collection type has no properties.");
                    Assert.AreEqual(0, c.CollectionType.KeyProperties.Count(), "Collection type has no properties.");
                    Assert.AreEqual(0, c.CollectionType.ETagProperties.Count(), "Collection type has no properties.");
                    Assert.IsNull(c.CollectionType.CustomState, "Custom state should be null by default.");

                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => c.CollectionType.IsMediaLinkEntry = true,
                        "Setting MLE on collection type should fail.");
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => c.CollectionType.IsOpenType = true,
                        "Setting IsOpenType on collection type should fail.");
                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => c.CollectionType.CanReflectOnInstanceType = false,
                        "Setting CanReflectOnInstanceType on collection type should fail.");
                    // Setting a custom state should still work
                    c.CollectionType.CustomState = "some value";
                    Assert.AreEqual("some value", c.CollectionType.CustomState, "Custom state doesn't persist its value.");

                    ExceptionUtils.ThrowsException<InvalidOperationException>(
                        () => c.CollectionType.AddProperty(new ResourceProperty("ID", ResourcePropertyKind.ComplexType, complexType)),
                        "Adding a property on collection type should fail.");
                    
                    c.CollectionType.SetReadOnly();
                    Assert.IsTrue(c.CollectionType.IsReadOnly, "The collection type is always read-only.");
                });

            // Verify that only primitive and complex types are allowed as items in a collection
            ResourceType collectionType = ResourceType.GetCollectionResourceType(complexType);
            foreach (var t in new ResourceType[] { entityType, collectionType })
            {
                Exception exception = AstoriaTestNS.TestUtil.RunCatching(() => ResourceType.GetCollectionResourceType(t));
                Assert.IsTrue(exception is ArgumentException, "Exception of a wrong type");
                Assert.AreEqual(
                    "Only collection properties that contain primitive types or complex types are supported.",
                    exception.Message, "Wrong exception message.");
            }
        }
        private void PopulateTestMetadata()
        {
            ResourceType entityType = new ResourceType(typeof(TestEntityType), ResourceTypeKind.EntityType, null, "AstoriaUnitTests.Tests.Server", "TestEntityType", false);
            entityType.CanReflectOnInstanceType = true;
            entityType.AddProperty(new ResourceProperty("ID", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Int32))));
            entityType.AddProperty(new ResourceProperty("Name", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(String))));
            entityType.AddProperty(new ResourceProperty("Spatial", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Geography))));
            entityType.AddProperty(new ResourceProperty("Point", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(GeographyPoint))));
            entityType.SetReadOnly();

            serviceFactory.AddResourceType(entityType);

            ResourceSet entitySet = new ResourceSet("Entities", entityType);
            entitySet.SetReadOnly();
            serviceFactory.AddResourceSet(entitySet);
        }
Exemple #25
0
        /// <summary>Adds a new resource property.</summary>
        /// <param name="resourceType">The resource type to add the property to.</param>
        /// <param name="name">The name of the property to add.</param>
        /// <param name="kind">The kind of the property to add.</param>
        /// <param name="propertyType">The type of the property to add.</param>
        /// <param name="propertyInfo">If this is a CLR property, the <see cref="PropertyInfo"/> for the property, or null otherwise.</param>
        /// <returns>The newly created and added property.</returns>
        private static ResourceProperty AddResourceProperty(
            ResourceType resourceType, 
            string name, 
            ResourcePropertyKind kind, 
            ResourceType propertyType, 
            PropertyInfo propertyInfo)
        {
            ResourceProperty property = new ResourceProperty(name, kind, propertyType);
            if (propertyInfo != null)
            {
                property.CanReflectOnInstanceTypeProperty = true;
                property.CustomState = new ResourcePropertyAnnotation() { PropertyInfo = propertyInfo };
            }
            else if (kind != ResourcePropertyKind.Stream)
            {
                property.CanReflectOnInstanceTypeProperty = false;
                property.CustomState = new ResourcePropertyAnnotation() { };
            }

            resourceType.AddProperty(property);
            return property;
        }
        private static void BuildTypeProperties(
            ResourceType parentResourceType,
            ProviderMetadataCacheItem metadataCacheItem,
            Queue<ResourceType> unvisitedTypes)
        {
            Debug.Assert(parentResourceType != null, "parentResourceType != null");
            Debug.Assert(metadataCacheItem != null, "metadataCacheItem != null");
            Debug.Assert(unvisitedTypes != null, "unvisitedTypes != null");

            BindingFlags bindingFlags = WebUtil.PublicInstanceBindingFlags;

            // For non root types, we should only look for properties that are declared for this type
            if (parentResourceType.BaseType != null)
            {
                bindingFlags = bindingFlags | BindingFlags.DeclaredOnly;
            }

            HashSet<string> propertiesToBeIgnored = new HashSet<string>(IgnorePropertiesAttribute.GetProperties(parentResourceType.InstanceType, false /*inherit*/, bindingFlags), StringComparer.Ordinal);
            Debug.Assert(parentResourceType.IsOpenType == false, "ReflectionServiceProvider does not support Open types.");

            HashSet<string> etagPropertyNames = new HashSet<string>(LoadETagProperties(parentResourceType), StringComparer.Ordinal);

            ResourceKeyKind keyKind = (ResourceKeyKind)Int32.MaxValue;
            PropertyInfo[] properties = parentResourceType.InstanceType.GetProperties(bindingFlags);

            // Should not allow System.Object on server
            // The general fix for this bug is to not support any resource type that doesn't have
            // any publically visible properties, including System.object and also custom types which don't have any properties.
            if (!properties.Any() && parentResourceType.BaseType == null)
            {
                throw new NotSupportedException(Strings.ReflectionProvider_ResourceTypeHasNoPublicallyVisibleProperties(parentResourceType.FullName));
            }

            foreach (PropertyInfo property in properties)
            {
                // Ignore the properties which are specified in the IgnoreProperties attribute
                if (propertiesToBeIgnored.Contains(property.Name))
                {
                    continue;
                }

                if (property.CanRead && property.GetIndexParameters().Length == 0)
                {
                    ResourcePropertyKind kind = (ResourcePropertyKind)(-1);
                    ResourceType resourceType;
                    Type resourcePropertyType = property.PropertyType;
                    bool collection = false;

                    if (!TryGetType(metadataCacheItem, resourcePropertyType, out resourceType))
                    {
                        Type collectionType = GetIEnumerableElement(property.PropertyType);
                        if (collectionType != null)
                        {
                            TryGetType(metadataCacheItem, collectionType, out resourceType);

                            // Even if the above method returns false, we should set the
                            // following variable appropriately, so that we can use them below
                            collection = true;
                            resourcePropertyType = collectionType;
                        }
                    }

                    if (resourceType != null)
                    {
                        #region Already Known Type
                        if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
                        {
                            if (collection)
                            {
                                // If it's a collection it can't be a key property (we don't allow collection properties as key)
                                kind = ResourcePropertyKind.Collection;
                            }
                            else
                            {
                                ResourceKeyKind currentKeyKind;
                                if (parentResourceType.BaseType == null && parentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType && IsPropertyKeyProperty(property, out currentKeyKind))
                                {
                                    // Check for key property only on root types, since keys must be defined on the root types
                                    if ((int)currentKeyKind < (int)keyKind)
                                    {
                                        if (parentResourceType.KeyProperties.Count != 0)
                                        {
                                            // Remove the existing property as key property - mark it as non key property
                                            parentResourceType.RemoveKeyProperties();
                                        }

                                        keyKind = currentKeyKind;
                                        kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive;
                                    }
                                    else if ((int)currentKeyKind == (int)keyKind)
                                    {
                                        Debug.Assert(currentKeyKind == ResourceKeyKind.AttributedKey, "This is the only way of specifying composite keys");
                                        kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive;
                                    }
                                    else
                                    {
                                        kind = ResourcePropertyKind.Primitive;
                                    }
                                }
                                else
                                {
                                    kind = ResourcePropertyKind.Primitive;
                                }
                            }
                        }
                        else if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                        {
                            kind = collection ? ResourcePropertyKind.Collection : ResourcePropertyKind.ComplexType;
                        }
                        else if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                        {
                            kind = collection ? ResourcePropertyKind.ResourceSetReference : ResourcePropertyKind.ResourceReference;
                        }
                        #endregion // Already Known Type
                    }
                    else
                    {
                        resourceType = IsEntityOrComplexType(resourcePropertyType, metadataCacheItem, unvisitedTypes);
                        if (resourceType != null)
                        {
                            if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                            {
                                if (collection)
                                {
                                    // For backward compat reasons only look closely on the item type if the property is a collection (IEnumerable<T>) 
                                    // and the T looks like a complex type.
                                    // In that case we used to only allow entity types as T, now with collection properties we also allow primitive and complex types,
                                    //   but we must explicitely disallow collection of collection, that is any T which implements IEnumerable<T>.
                                    // Note that we can't just make IEnumerable<T> not be a complex types since in certain cases we used to recognize it as such
                                    //   and we would break those cases. One example is user registered known types.
                                    Type collectionType = GetIEnumerableElement(resourcePropertyType);
                                    if (collectionType != null)
                                    {
                                        throw new InvalidOperationException(Strings.ReflectionProvider_CollectionOfCollectionProperty(property.Name, parentResourceType.FullName));
                                    }

                                    kind = ResourcePropertyKind.Collection;
                                }
                                else
                                {
                                    kind = ResourcePropertyKind.ComplexType;
                                }
                            }
                            else
                            {
                                Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Must be an entity type");
                                kind = collection ? ResourcePropertyKind.ResourceSetReference : ResourcePropertyKind.ResourceReference;
                            }
                        }
                    }

                    // if resource type is null OR
                    // if complex type has a property of entity type
                    if (resourceType == null ||
                        (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType && parentResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType))
                    {
                        if (resourceType == null)
                        {
                            // Provide a better error message for collection of collection
                            if (collection && GetIEnumerableElement(resourcePropertyType) != null)
                            {
                                throw new InvalidOperationException(Strings.ReflectionProvider_CollectionOfCollectionProperty(property.Name, parentResourceType.FullName));
                            }

                            // Provide a better error message for collection of wrong types
                            if (collection)
                            {
                                throw new InvalidOperationException(Strings.ReflectionProvider_CollectionOfUnsupportedTypeProperty(property.Name, parentResourceType.FullName, resourcePropertyType));
                            }

                            if (CommonUtil.IsUnsupportedType(resourcePropertyType))
                            {
                                throw new InvalidOperationException(Strings.BadProvider_UnsupportedPropertyType(property.Name, parentResourceType.FullName));
                            }

                            throw new InvalidOperationException(Strings.ReflectionProvider_InvalidProperty(property.Name, parentResourceType.FullName));
                        }

                        // Navigation property on a complex type is not supported
                        throw new InvalidOperationException(Strings.ReflectionProvider_ComplexTypeWithNavigationProperty(property.Name, parentResourceType.FullName));
                    }

                    if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                    {
                        ResourceSet container = InternalGetContainerForResourceType(resourcePropertyType, metadataCacheItem.EntitySets.Values);
                        if (container == null)
                        {
                            throw new InvalidOperationException(Strings.ReflectionProvider_EntityPropertyWithNoEntitySet(parentResourceType.FullName, property.Name));
                        }
                    }

                    if (kind == ResourcePropertyKind.Collection)
                    {
                        // Collection properties need the collection type (representing the IEnumerable<> part).
                        resourceType = ResourceType.GetCollectionResourceType(resourceType);
                    }

                    if (etagPropertyNames.Remove(property.Name))
                    {
                        kind |= ResourcePropertyKind.ETag;
                    }

                    ResourceProperty resourceProperty = new ResourceProperty(property.Name, kind, resourceType);
                    MimeTypeAttribute attribute = BaseServiceProvider.GetMimeTypeAttribute(property);
                    if (attribute != null)
                    {
                        resourceProperty.MimeType = attribute.MimeType;
                    }

                    parentResourceType.AddProperty(resourceProperty);
                }
                else
                {
                    throw new InvalidOperationException(Strings.ReflectionProvider_InvalidProperty(property.Name, parentResourceType.FullName));
                }
            }

            if (parentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType &&
                (parentResourceType.KeyProperties == null || parentResourceType.KeyProperties.Count == 0))
            {
                throw new InvalidOperationException(Strings.ReflectionProvider_KeyPropertiesCannotBeIgnored(parentResourceType.FullName));
            }

            if (etagPropertyNames.Count != 0)
            {
                throw new InvalidOperationException(Strings.ReflectionProvider_ETagPropertyNameNotValid(etagPropertyNames.ElementAt(0), parentResourceType.FullName));
            }
        }
        private static DataServiceProviderWrapper CreateProvider(out DataServiceConfiguration config, out DataServiceOperationContext operationContext)
        {
            var baseUri = new Uri("http://localhost");
            var host = new DataServiceHostSimulator()
            {
                AbsoluteServiceUri = baseUri,
                AbsoluteRequestUri = new Uri(baseUri.AbsoluteUri + "/$metadata", UriKind.Absolute),
                RequestHttpMethod = "GET",
                RequestAccept = "application/xml+atom",
                RequestVersion = "4.0",
                RequestMaxVersion = "4.0",
            };

            operationContext = new DataServiceOperationContext(host);
            var dataService = new DataServiceSimulator() { OperationContext = operationContext };
            operationContext.InitializeAndCacheHeaders(dataService);

            DataServiceProviderSimulator providerSimulator = new DataServiceProviderSimulator();
            providerSimulator.ContainerNamespace = "MyModel";
            providerSimulator.ContainerName = "CustomersContainer";

            ResourceType customerEntityType = new ResourceType(
                typeof(object), ResourceTypeKind.EntityType, null, "MyModel", "Customer", false)
            { 
                CanReflectOnInstanceType = false 
            };

            ResourcePropertyKind idPropertyKind = ResourcePropertyKind.Primitive | ResourcePropertyKind.Key;
            ResourceProperty idProperty = new ResourceProperty(
                "Id", idPropertyKind, ResourceType.GetPrimitiveResourceType(typeof(int)))
            { 
                CanReflectOnInstanceTypeProperty = false 
            };
            customerEntityType.AddProperty(idProperty);

            ResourcePropertyKind firstNamePropertyKind = ResourcePropertyKind.Primitive | ResourcePropertyKind.Key;
            ResourceProperty firstNameProperty = new ResourceProperty(
                "FirstName", firstNamePropertyKind, ResourceType.GetPrimitiveResourceType(typeof(string)))
            {
                CanReflectOnInstanceTypeProperty = false
            };
            customerEntityType.AddProperty(firstNameProperty);            
            
            customerEntityType.SetReadOnly();
            providerSimulator.AddResourceType(customerEntityType);

            ResourceSet customerSet = new ResourceSet("Customers", customerEntityType);
            customerSet.SetReadOnly();
            providerSimulator.AddResourceSet(customerSet);

            config = new DataServiceConfiguration(providerSimulator);
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = ODataProtocolVersion.V4;

            IDataServiceProviderBehavior providerBehavior = DataServiceProviderBehavior.CustomDataServiceProviderBehavior;
            DataServiceStaticConfiguration staticConfig = new DataServiceStaticConfiguration(dataService.Instance.GetType(), providerSimulator);

            DataServiceProviderWrapper provider = new DataServiceProviderWrapper(
                new DataServiceCacheItem(config, staticConfig), providerSimulator, providerSimulator, dataService, false);

            dataService.ProcessingPipeline = new DataServiceProcessingPipeline();
            dataService.Provider = provider;
            provider.ProviderBehavior = providerBehavior;
            dataService.ActionProvider = DataServiceActionProviderWrapper.Create(dataService);
#if DEBUG
            dataService.ProcessingPipeline.SkipDebugAssert = true;
#endif
            operationContext.RequestMessage.InitializeRequestVersionHeaders(VersionUtil.ToVersion(config.DataServiceBehavior.MaxProtocolVersion));
            return provider;
        }
        public void ServiceActionConstructorTests()
        {
            ResourceProperty id = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int)));
            ResourceType customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "Customer", false);
            customerType.AddProperty(id);
            customerType.SetReadOnly();
            ResourceSet customerSet = new ResourceSet("Customers", customerType);
            ResourceType orderType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Foo", "Order", false);
            orderType.AddProperty(id);
            orderType.SetReadOnly();

            ResourceType complexType = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "foo", "Address", false);
            ResourceType collectionOfPrimitive = ResourceType.GetCollectionResourceType(ResourceType.GetPrimitiveResourceType(typeof(int)));
            ResourceType collectionOfComplex = ResourceType.GetCollectionResourceType(complexType);
            ResourceType collectionOfCustomer = ResourceType.GetEntityCollectionResourceType(customerType);
            ResourceType collectionOfOrder = ResourceType.GetEntityCollectionResourceType(orderType);

            var types = ResourceTypeUtils.GetPrimitiveResourceTypes().Concat(new ResourceType[] { null, customerType, orderType, complexType, collectionOfPrimitive, collectionOfComplex, collectionOfCustomer, collectionOfOrder });
            var resultSetsOrPathExpressions = new object[] { null, customerSet, new ResourceSetPathExpression("p1/Foo") };
            var parameters = types.Except(new[] { ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)) }).Select(t => t != null ? new ServiceActionParameter[] { new ServiceActionParameter("p1", t) } : new ServiceActionParameter[0]);
            parameters = parameters.Concat(types.Except(new[] { ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)), null }).ToList().Combinations(2).Select(tt => new ServiceActionParameter[] { new ServiceActionParameter("p1", tt[0]), new ServiceActionParameter("p2", tt[1]) }));
            var operationParameterBindings = new OperationParameterBindingKind[] { OperationParameterBindingKind.Never, OperationParameterBindingKind.Sometimes, OperationParameterBindingKind.Always };

            AstoriaTestNS.TestUtil.RunCombinations(
                types, resultSetsOrPathExpressions, parameters, operationParameterBindings,
                (returnType, resultSetOrPathExpression, paramList, operationParameterBindingKind) =>
            {
                ServiceAction action = null;
                ResourceSet resourceSet = resultSetOrPathExpression as ResourceSet;
                ResourceSetPathExpression pathExpression = resultSetOrPathExpression as ResourceSetPathExpression;
                bool bindable = (operationParameterBindingKind == OperationParameterBindingKind.Always || operationParameterBindingKind == OperationParameterBindingKind.Sometimes);
                Exception e = null;

                try
                {
                    if (pathExpression != null)
                    {
                        bindable = true;
                        action = new ServiceAction("foo", returnType, OperationParameterBindingKind.Sometimes, paramList, pathExpression);
                    }
                    else
                    {
                        action = new ServiceAction("foo", returnType, resourceSet, operationParameterBindingKind, paramList);
                    }
                }
                catch (Exception ex)
                {
                    e = ex;
                }
                if (resourceSet != null && operationParameterBindingKind != OperationParameterBindingKind.Never)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "When 'returnType' is an entity type or an entity collection type, 'resultSetPathExpression' and 'resultSet' cannot be both null and the resource type of the result set must be assignable from 'returnType'.");
                }
                else if (returnType != null && (returnType.ResourceTypeKind == ResourceTypeKind.EntityType || returnType.ResourceTypeKind == ResourceTypeKind.EntityCollection) &&
                    (resourceSet == null && pathExpression == null ||
                    resourceSet != null && returnType.ResourceTypeKind == ResourceTypeKind.EntityType && resourceSet.ResourceType != returnType ||
                    resourceSet != null && returnType.ResourceTypeKind == ResourceTypeKind.EntityCollection && resourceSet.ResourceType != ((EntityCollectionResourceType)returnType).ItemType))
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "When 'returnType' is an entity type or an entity collection type, 'resultSetPathExpression' and 'resultSet' cannot be both null and the resource type of the result set must be assignable from 'returnType'.");
                }
                else if ((returnType == null || returnType.ResourceTypeKind != ResourceTypeKind.EntityCollection && returnType.ResourceTypeKind != ResourceTypeKind.EntityType) && resourceSet != null)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "'resultSet' must be null when 'returnType' is null, not an entity type or not an entity collection type.");
                }
                else if ((returnType == null || returnType.ResourceTypeKind != ResourceTypeKind.EntityCollection && returnType.ResourceTypeKind != ResourceTypeKind.EntityType) && pathExpression != null)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "'resultSetPathExpression' must be null when 'returnType' is null, not an entity type or not an entity collection type.");
                }
                else if (returnType == ResourceType.GetPrimitiveResourceType(typeof(System.IO.Stream)))
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "The resource type 'Edm.Stream' is not a type that can be returned by a function or action. A function or action can only return values of an entity type, an entity collection type, a complex type, a collection type or any primitive type, other than the stream type.\r\nParameter name: returnType");
                }
                else if (paramList.Length > 0 && paramList.Skip(bindable ? 1 : 0).Any(p => p.ParameterType.ResourceTypeKind == ResourceTypeKind.EntityType || p.ParameterType.ResourceTypeKind == ResourceTypeKind.EntityCollection))
                {
                    var param = paramList.Skip(bindable ? 1 : 0).First(p => p.ParameterType.ResourceTypeKind == ResourceTypeKind.EntityType || p.ParameterType.ResourceTypeKind == ResourceTypeKind.EntityCollection);
                    var parameterTypeKind = param.ParameterType.ResourceTypeKind;
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, string.Format("The '{0}' parameter is of resource type kind '{1}' and it is not the binding parameter. Parameter of type kind '{1}' is only supported for the binding parameter.", param.Name, parameterTypeKind));
                }
                else if (pathExpression != null && !bindable)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "The binding parameter type must be an entity type or an entity collection type when 'resultSetPathExpression' is not null.");
                }
                else if (bindable && paramList.Length == 0)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "Bindable actions or functions must have at least one parameter, where the first parameter is the binding parameter.\r\nParameter name: operationParameterBindingKind");
                }
                else if (pathExpression != null && bindable && paramList.First().ParameterType.ResourceTypeKind != ResourceTypeKind.EntityType && paramList.First().ParameterType.ResourceTypeKind != ResourceTypeKind.EntityCollection)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "The binding parameter type must be an entity type or an entity collection type when 'resultSetPathExpression' is not null.");
                }
                else if (paramList.Length > 0 && bindable && paramList[0].ParameterType.ResourceTypeKind != ResourceTypeKind.EntityType && paramList[0].ParameterType.ResourceTypeKind != ResourceTypeKind.EntityCollection)
                {
                    ExceptionUtils.IsExpectedException<ArgumentException>(e, "An action's binding parameter must be of type Entity or EntityCollection.\r\nParameter name: parameters");
                }
                else
                {
                    Assert.IsNull(e, "Received exception but expected none. Exception message: {0}", e == null ? string.Empty : e.Message);
                    Assert.IsNotNull(action, "Action should be constructed.");

                    Assert.AreEqual("foo", action.Name, "unexpected name");
                    Assert.AreEqual(returnType, action.ReturnType, "unexpected return type");
                    Assert.AreEqual(resourceSet, action.ResourceSet, "unexpected result set");
                    Assert.AreEqual(pathExpression, action.ResultSetPathExpression, "unexpected path expression");
                    Assert.IsTrue(!bindable || action.BindingParameter == action.Parameters.First(), "unexpected binding parameter");
                    Assert.IsTrue(action.Method == "POST", "HttpMethod must be POST for ServiceActions.");
                    Assert.IsTrue(action.ResourceSet == null || action.ResultSetPathExpression == null, "'resultSet' and 'resultSetPathExpression' cannot be both set by the constructor.");
                }
            });
        }