public static void Register(HttpConfiguration httpConfiguration) { ODataModelBuilder builder = new ODataConventionModelBuilder(); builder.Namespace = "ODataV4TestService.Models"; builder.EntitySet<Product>("Products"); builder.EntitySet<SuppliedProduct>("SuppliedProducts"); builder.EntitySet<Supplier>("Suppliers"); builder.EntitySet<Product>("OtherProducts"); builder.ComplexType<Description>(); builder.EntityType<Product>() .Action("Rate") .Parameter<int>("Rating"); builder.EntityType<Product>().Collection .Function("Best") .ReturnsCollectionFromEntitySet<Product>("Products"); var funcConfig = builder .EntityType<Product>() .Function("RelatedProducts") .SetBindingParameter("product", builder.GetTypeConfigurationOrNull(typeof(Product))) //.AddParameter("start", new PrimitiveTypeConfiguration(builder, builder.GetTypeConfigurationOrNull(typeof(DateTimeOffset)), typeof(DateTimeOffset)). .ReturnsCollectionFromEntitySet<Product>("Products"); funcConfig .Parameter<DateTimeOffset>("start"); funcConfig .Parameter<DateTimeOffset>("end"); //builder.Function("GetSalesTaxRate") // .Returns<double>() // .Parameter<int>("PostalCode"); builder.EntitySet<Account>("Accounts"); builder.EntityType<PaymentInstrument>() .Collection .Function("GetCount") .Returns<int>() .Parameter<string>("NameContains"); var model = builder.GetEdmModel(); var conventions = ODataRoutingConventions.CreateDefault(); conventions.Insert(0, new AttributeRoutingConvention(model, httpConfiguration)); var server = new BatchServer(httpConfiguration); httpConfiguration.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: null, model: model, pathHandler: new DefaultODataPathHandler(), routingConventions: conventions, batchHandler: new DefaultODataBatchHandler(server)); httpConfiguration.MessageHandlers.Add(new TracingMessageHandler()); }
/// <inheritdoc/> public async Task <IEdmModel> GetModelAsync(ModelContext context, CancellationToken cancellationToken) { // This means user build a model with customized model builder registered as inner most // Its element will be added to built model. IEdmModel innerModel = null; if (InnerModelBuilder != null) { innerModel = await InnerModelBuilder.GetModelAsync(context, cancellationToken).ConfigureAwait(false); } var entitySetTypeMap = context.ResourceSetTypeMap; if (entitySetTypeMap == null || entitySetTypeMap.Count == 0) { return(innerModel); } // Collection of entity type and set name is set by EF now, // and EF model producer will not build model any more // Web Api OData conversion model built is been used here, // refer to Web Api OData document for the detail conversions been used for model built. var builder = new ODataConventionModelBuilder { // This namespace is used by container Namespace = entitySetTypeMap.First().Value.Namespace }; var method = typeof(ODataConventionModelBuilder).GetMethod("EntitySet", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); foreach (var pair in entitySetTypeMap) { // Build a method with the specific type argument var specifiedMethod = method.MakeGenericMethod(pair.Value); var parameters = new object[] { pair.Key }; specifiedMethod.Invoke(builder, parameters); } entitySetTypeMap.Clear(); var entityTypeKeyPropertiesMap = context.ResourceTypeKeyPropertiesMap; if (entityTypeKeyPropertiesMap != null) { foreach (var pair in entityTypeKeyPropertiesMap) { var edmTypeConfiguration = builder.GetTypeConfigurationOrNull(pair.Key) as EntityTypeConfiguration; if (edmTypeConfiguration == null) { continue; } foreach (var property in pair.Value) { edmTypeConfiguration.HasKey(property); } } entityTypeKeyPropertiesMap.Clear(); } var model = (EdmModel)builder.GetEdmModel(); // Add all Inner model content into existing model // When WebApi OData make conversion model builder accept an existing model, this can be removed. if (innerModel != null) { foreach (var element in innerModel.SchemaElements) { if (!(element is EdmEntityContainer)) { model.AddElement(element); } } foreach (var annotation in innerModel.VocabularyAnnotations) { model.AddVocabularyAnnotation(annotation); } var entityContainer = (EdmEntityContainer)model.EntityContainer; var innerEntityContainer = (EdmEntityContainer)innerModel.EntityContainer; if (innerEntityContainer != null) { foreach (var entityset in innerEntityContainer.EntitySets()) { if (entityContainer.FindEntitySet(entityset.Name) == null) { entityContainer.AddEntitySet(entityset.Name, entityset.EntityType()); } } foreach (var singleton in innerEntityContainer.Singletons()) { if (entityContainer.FindEntitySet(singleton.Name) == null) { entityContainer.AddSingleton(singleton.Name, singleton.EntityType()); } } foreach (var operation in innerEntityContainer.OperationImports()) { if (entityContainer.FindOperationImports(operation.Name) == null) { if (operation.IsFunctionImport()) { entityContainer.AddFunctionImport( operation.Name, (EdmFunction)operation.Operation, operation.EntitySet); } else { entityContainer.AddActionImport( operation.Name, (EdmAction)operation.Operation, operation.EntitySet); } } } } } return(model); }
public static void Register(HttpConfiguration httpConfiguration) { ODataModelBuilder builder = new ODataConventionModelBuilder(); builder.Namespace = "ODataV4TestService.Models"; builder.EntitySet <Product>("Products"); builder.EntitySet <SuppliedProduct>("SuppliedProducts"); builder.EntitySet <Supplier>("Suppliers"); builder.EntitySet <Product>("OtherProducts"); builder.ComplexType <Description>(); builder.EntityType <Product>() .Action("Rate") .Parameter <int>("Rating"); builder.EntityType <Product>().Collection .Function("Best") .ReturnsCollectionFromEntitySet <Product>("Products"); var funcConfig = builder .EntityType <Product>() .Function("RelatedProducts") .SetBindingParameter("product", builder.GetTypeConfigurationOrNull(typeof(Product))) //.AddParameter("start", new PrimitiveTypeConfiguration(builder, builder.GetTypeConfigurationOrNull(typeof(DateTimeOffset)), typeof(DateTimeOffset)). .ReturnsCollectionFromEntitySet <Product>("Products"); funcConfig .Parameter <DateTimeOffset>("start"); funcConfig .Parameter <DateTimeOffset>("end"); //builder.Function("GetSalesTaxRate") // .Returns<double>() // .Parameter<int>("PostalCode"); builder.EntitySet <Account>("Accounts"); builder.EntityType <PaymentInstrument>() .Collection .Function("GetCount") .Returns <int>() .Parameter <string>("NameContains"); var model = builder.GetEdmModel(); var conventions = ODataRoutingConventions.CreateDefault(); conventions.Insert(0, new AttributeRoutingConvention(model, httpConfiguration)); var server = new BatchServer(httpConfiguration); httpConfiguration.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: null, model: model, pathHandler: new DefaultODataPathHandler(), routingConventions: conventions, batchHandler: new DefaultODataBatchHandler(server)); httpConfiguration.MessageHandlers.Add(new TracingMessageHandler()); }
private static void MapODataControllerActions(ODataConventionModelBuilder builder, Type ctrlType, EntityTypeConfiguration entityTypeConfig, EntitySetConfiguration entitySetConfig) { // Map actions var methods = ctrlType.GetMethods().Where(m => m.GetCustomAttribute <ODataProcedureAttribute>() != null); foreach (var method in methods) { var attr = method.GetCustomAttribute <ODataProcedureAttribute>(); ProcedureConfiguration procedure; // TODO: Extract into separate class if possible Func <string, IEdmTypeConfiguration, ProcedureConfiguration> setBindingParameterFunc; string previousNamespace = builder.Namespace; builder.Namespace = attr.Namespace; if (attr.Type == ODataProcedureType.Action) { // Create an action procedure = builder.Action(attr.ProcedureName ?? method.Name); setBindingParameterFunc = ((ActionConfiguration)procedure).SetBindingParameter; } else { // Create a function procedure = builder.Function(attr.ProcedureName ?? method.Name); setBindingParameterFunc = ((FunctionConfiguration)procedure).SetBindingParameter; } builder.Namespace = previousNamespace; if (attr.Target == ODataProcedureTarget.Entity) { // Bind to entity setBindingParameterFunc(BindingParameterConfiguration.DefaultBindingParameterName, entityTypeConfig); } else { // Bind to collection var entityCollectionConfig = new CollectionTypeConfiguration(entityTypeConfig, typeof(Enumerable)); setBindingParameterFunc(BindingParameterConfiguration.DefaultBindingParameterName, entityCollectionConfig); } var parameterAttrs = method.GetCustomAttributes <ODataProcedureParameterAttribute>(); foreach (var parameterAttr in parameterAttrs) { var parameterType = parameterAttr.Type; // Is the parameter type a collection? if (parameterType.IsGenericType && parameterType.GetInterface(typeof(IEnumerable).Name, false) != null) { // If the parameter is a collection, we need to call the generic CollectionParameter<T>(name) method var genericArgument = parameterType.GetGenericArguments().Single(); var parameterMethod = procedure.GetType().GetMethod("CollectionParameter"); var methodInfo = parameterMethod.MakeGenericMethod(genericArgument); methodInfo.Invoke(procedure, new object[] { parameterAttr.Name }); } else { // If the parameter is not a collection, we call the generic Parameter<T>(name) method var parameterMethod = procedure.GetType().GetMethod("Parameter"); var methodInfo = parameterMethod.MakeGenericMethod(parameterType); methodInfo.Invoke(procedure, new object[] { parameterAttr.Name }); } } if (attr.Returns != null) { var returns = procedure.GetType().GetMethod("Returns"); var generic = returns.MakeGenericMethod(attr.Returns); generic.Invoke(procedure, null); } else if (attr.ReturnsCollection != null) { var returnsCollection = procedure.GetType().GetMethod("ReturnsCollection"); var generic = returnsCollection.MakeGenericMethod(attr.ReturnsCollection); generic.Invoke(procedure, null); } else if (attr.ReturnsEntity != null) { var returnsEntity = procedure.GetType().GetMethod("ReturnsFromEntitySet", new Type[] { typeof(string) }); var generic = returnsEntity.MakeGenericMethod(attr.ReturnsEntity); var entitySet = builder.EntitySets.Single(set => set.ClrType == attr.ReturnsEntity); generic.Invoke(procedure, new object[] { entitySet.Name }); } else if (attr.ReturnsEntityCollection != null) { var returnsEntity = procedure.GetType().GetMethod("ReturnsCollectionFromEntitySet", new Type[] { typeof(string) }); var generic = returnsEntity.MakeGenericMethod(attr.ReturnsEntityCollection); var entitySet = builder.EntitySets.Single(set => set.ClrType == attr.ReturnsEntityCollection); generic.Invoke(procedure, new object[] { entitySet.Name }); } else { procedure.ReturnType = builder.GetTypeConfigurationOrNull(method.ReturnType); } } }
/// <inheritdoc/> public async Task<IEdmModel> GetModelAsync(ModelContext context, CancellationToken cancellationToken) { // This means user build a model with customized model builder registered as inner most // Its element will be added to built model. IEdmModel innerModel = null; if (InnerModelBuilder != null) { innerModel = await InnerModelBuilder.GetModelAsync(context, cancellationToken); } var entitySetTypeMap = context.ResourceSetTypeMap; if (entitySetTypeMap == null || entitySetTypeMap.Count == 0) { return innerModel; } // Collection of entity type and set name is set by EF now, // and EF model producer will not build model any more // Web Api OData conversion model built is been used here, // refer to Web Api OData document for the detail conversions been used for model built. var builder = new ODataConventionModelBuilder(); // This namespace is used by container builder.Namespace = entitySetTypeMap.First().Value.Namespace; MethodInfo method = typeof(ODataConventionModelBuilder) .GetMethod("EntitySet", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); foreach (var pair in entitySetTypeMap) { // Build a method with the specific type argument var specifiedMethod = method.MakeGenericMethod(pair.Value); var parameters = new object[] { pair.Key }; specifiedMethod.Invoke(builder, parameters); } entitySetTypeMap.Clear(); var entityTypeKeyPropertiesMap = context.ResourceTypeKeyPropertiesMap; if (entityTypeKeyPropertiesMap != null) { foreach (var pair in entityTypeKeyPropertiesMap) { var edmTypeConfiguration = builder.GetTypeConfigurationOrNull(pair.Key) as EntityTypeConfiguration; if (edmTypeConfiguration == null) { continue; } foreach (var property in pair.Value) { edmTypeConfiguration.HasKey(property); } } entityTypeKeyPropertiesMap.Clear(); } var model = (EdmModel)builder.GetEdmModel(); // Add all Inner model content into existing model // When WebApi OData make conversion model builder accept an existing model, this can be removed. if (innerModel != null) { foreach (var element in innerModel.SchemaElements) { if (!(element is EdmEntityContainer)) { model.AddElement(element); } } foreach (var annotation in innerModel.VocabularyAnnotations) { model.AddVocabularyAnnotation(annotation); } var entityContainer = (EdmEntityContainer)model.EntityContainer; var innerEntityContainer = (EdmEntityContainer)innerModel.EntityContainer; if (innerEntityContainer != null) { foreach (var entityset in innerEntityContainer.EntitySets()) { if (entityContainer.FindEntitySet(entityset.Name) == null) { entityContainer.AddEntitySet(entityset.Name, entityset.EntityType()); } } foreach (var singleton in innerEntityContainer.Singletons()) { if (entityContainer.FindEntitySet(singleton.Name) == null) { entityContainer.AddSingleton(singleton.Name, singleton.EntityType()); } } foreach (var operation in innerEntityContainer.OperationImports()) { if (entityContainer.FindOperationImports(operation.Name) == null) { if (operation.IsFunctionImport()) { entityContainer.AddFunctionImport( operation.Name, (EdmFunction)operation.Operation, operation.EntitySet); } else { entityContainer.AddActionImport( operation.Name, (EdmAction)operation.Operation, operation.EntitySet); } } } } } return model; }