I've seen the example at https://github.com/OData/ODataSamples/tree/master/WebApi/v4/DynamicEdmModelCreation for dynamic Odata route and model generation.
I need the same for dot net core but since Asp.net and Asp.net core vary in terms of the classes/routing etc. I'm unable to translate this code to dot net core.
Can anybody provide some guidance ?
My Startup.cs Configure method has this -
IEdmModel model = this.GetEdmModel(app.ApplicationServices);
IList<IODataRoutingConvention> routingConventions = new List<IODataRoutingConvention>();
routingConventions.Insert(0, new MatchAllRoutingConvention());
app.UseMvc(routeBuilder => routeBuilder.MapODataServiceRoute("odata", "odata", model, new CustomPathHandler(), routingConventions));
CustomPathHandler.cs -
public class CustomPathHandler : DefaultODataPathHandler
{
public override ODataPath Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer)
{
var path = base.Parse(serviceRoot, odataPath, requestContainer);
return path;
}
public override string Link(ODataPath path)
{
return base.Link(path);
}
}
ODataEndpointController.cs -
public class ODataEndpointController : ODataController
{
[EnableQuery]
[ODataRoute]
public EdmEntityObjectCollection Get()
{
// Get entity set's EDM type: A collection type.
ODataPath path = Request.ODataFeature().Path;
IEdmCollectionType collectionType = (IEdmCollectionType)path.EdmType;
IEdmEntityTypeReference entityType = collectionType.ElementType.AsEntity();
// Create an untyped collection with the EDM collection type.
EdmEntityObjectCollection collection =
new EdmEntityObjectCollection(new EdmCollectionTypeReference(collectionType));
// Add untyped objects to collection.
DataSourceProvider.Get(entityType, collection);
return collection;
}
MatchAllRoutingConvention.cs
public class MatchAllRoutingConvention : IODataRoutingConvention
{
public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext)
{
ControllerActionDescriptor test = new ControllerActionDescriptor();
test.ControllerName = "ODataEndpoint";
return new List<ControllerActionDescriptor> { test }.AsEnumerable();
}
}
Is there anything I'm doing wrong here ? When I try to hit http://localhost:port/odata/Products I get some source is null error
Edit (15-Jan):
Modifying the MatchAllRoutingConvention as below redirected the routing as required but so do any $metadata requests (which throw an exception). And $filter queries don't work either. So any pointers/tips would be helpful -
public class MatchAllRoutingConvention : IODataRoutingConvention
{
public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext)
{
ControllerActionDescriptor odataControllerDescriptor = new ControllerActionDescriptor
{
ControllerName = "ODataEndpoint",
ActionName = "Get",
Parameters = new List<ParameterDescriptor>(),
FilterDescriptors = new List<FilterDescriptor>(),
BoundProperties = new List<ParameterDescriptor>(),
MethodInfo = typeof(ODataEndpointController).GetMethod("Get"),
ControllerTypeInfo = typeof(ODataEndpointController).GetTypeInfo()
};
return new List<ControllerActionDescriptor> { odataControllerDescriptor };
}
}
Posting this as an answer since I haven't seen any responses and hopefully this would help someone. I modified the MatchAllRoutingConvention class as below -
public class MatchAllRoutingConvention : IODataRoutingConvention
{
public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext)
{
if (routeContext.RouteData.Values["odataPath"] == null ||
routeContext.RouteData.Values["odataPath"].ToString() == "$metadata")
return new MetadataRoutingConvention().SelectAction(routeContext);
IActionDescriptorCollectionProvider actionCollectionProvider =
routeContext.HttpContext.RequestServices.GetRequiredService<IActionDescriptorCollectionProvider>();
IEnumerable<ControllerActionDescriptor> actionDescriptors = actionCollectionProvider
.ActionDescriptors.Items.OfType<ControllerActionDescriptor>()
.Where(c => c.ControllerName == "ODataEndpoint");
return actionDescriptors;
}
}
This serves metadata requests correctly as well as simple entity requests. This doesn't however respond to $filter, $select etc. From what I understand the ODataQueryOptions class is used to apply OData filtering etc but they won't work on non-CLR classes which this is. So not sure if there is a solution for this problem.
[Edit] So I've managed a sort of hack to get around this filtering issue. There is this library LinqToQueryString which you apply on an IQueryable, not EdmEntityObjectCollection obviously (it's not that easy).
I first load my data into a Dictionary, there's an example on their site to do that. Apply the OData query options string on that dictionary first, Then I Load the filtered result into my entities and then into the EdmEntityObjectCollection
var list = new List<IEdmEntityObject>();
var dataList = new List<Dictionary<string, object>>();
// Load data in Dictionary
foreach (var item in data)
{
var dataItem = new Dictionary<string, object>();
// Load data
dataList.Add(dataItem);
}
var filteredDataList = dataList.AsQueryable().LinqToQuerystring(oDataQuery);
// Now add filtered/sorted etc data in EdmEntityObject
foreach (var item in filteredDataList)
{
var entity = new EdmEntityObject(entityType);
foreach (var key in item.Keys)
entity.TrySetPropertyValue(key, item[key].ToString());
list.Add(entity);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With