ASP.NET MVC Web API and oData Request Transfer

I am currently running a web API with oData filter requests as follows:

public IQueryable<OrganizationViewModel> Get(ODataQueryOptions<Organization> oDataQuery) { var query = new FindOrganizationsQuery(oDataQuery); var result =_findOrganizationsQueryHandler.Execute(query); return result.Organizations.Select(o => new OrganizationViewModel { Id = o.PublicId, Name = o.Name }); } 

The handler looks like this:

 public FindOrganizationsQueryResult Execute(FindOrganizationsQuery request) { var organizations = request.ODataQuery.ApplyTo(_mgpQueryContext.Organizations).Cast<Organization>(); return new FindOrganizationsQueryResult(organizations); } 

And the request class looks like this:

 public class FindOrganizationsQuery { public FindOrganizationsQuery(ODataQueryOptions<Organization> oDataQuery) { ODataQuery = oDataQuery; } public ODataQueryOptions<Organization> ODataQuery { get; set; } } 

So, if I pass the oData filter with the request, it handles nicely, and it all works great.

But now instead of passing the ODataQueryOptions type to the Get operation, I would like to have a FindOrganizationsQuery class, for example:

 public IQueryable<OrganizationViewModel> FindOrganizations(FindOrganizationsQuery query) { // query is null } 

However, query parameters are always zero. How to pass oData filter if ODataQueryOptions parameters are in another class?

+4
source share
1 answer

You can write the custom parameter binding attribute for FindOrganizationsQuery in the same way that we do for ODataQueryOptions , and then assign your FindOrganizationsQuery with this attribute.

Sample code below

 public class CustomQueryBindingAttribute : ParameterBindingAttribute { public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter) { return new CustomQueryBinding(parameter); } internal class CustomQueryBinding : HttpParameterBinding { public CustomQueryBinding(HttpParameterDescriptor parameter) : base(parameter) { } internal class CustomQueryBinding : HttpParameterBinding { public CustomQueryBinding(HttpParameterDescriptor parameter) : base(parameter) { } public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) { IEdmModel model = actionContext.Request.GetEdmModel() ?? actionContext.ActionDescriptor.GetEdmModel(typeof(Organization)); ODataQueryContext queryContext = new ODataQueryContext(model, typeof(Organization)); object customQuery = CreateCustomQuery(queryContext, actionContext.Request); SetValue(actionContext, customQuery); return Task.FromResult(0); } private object CreateCustomQuery(ODataQueryContext queryContext, HttpRequestMessage request) { Type parameterType = Descriptor.ParameterType; // Assuming all custom queries have this public property. Type oDataQueryOptionsOfTType = parameterType.GetProperty("ODataQuery").PropertyType; object odataQueryOptions = Activator.CreateInstance(oDataQueryOptionsOfTType, queryContext, request); return Activator.CreateInstance(parameterType, odataQueryOptions); } } } 

and the extension method that I copied from the web API source code, as it is not publicly available.

 public static class HttpActionDescriptorExtensions { internal const string EdmModelKey = "MS_EdmModel"; internal static IEdmModel GetEdmModel(this HttpActionDescriptor actionDescriptor, Type entityClrType) { // save the EdmModel to the action descriptor return actionDescriptor.Properties.GetOrAdd(EdmModelKey + entityClrType.FullName, _ => { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(actionDescriptor.Configuration, isQueryCompositionMode: true); EntityTypeConfiguration entityTypeConfiguration = builder.AddEntity(entityClrType); builder.AddEntitySet(entityClrType.Name, entityTypeConfiguration); IEdmModel edmModel = builder.GetEdmModel(); return edmModel; }) as IEdmModel; } } 

I have a complete sample here .

+1
source

Source: https://habr.com/ru/post/1497202/


All Articles