ASP.NET MVC Web API和oData查询传递

4

我目前正在执行使用oData过滤请求的Web API,如下所示:

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 });
}

处理程序的样式如下:
public FindOrganizationsQueryResult Execute(FindOrganizationsQuery request)
{
    var organizations = request.ODataQuery.ApplyTo(_mgpQueryContext.Organizations).Cast<Organization>();            
    return new FindOrganizationsQueryResult(organizations);
}

查询类的样例代码如下:

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

如果我在请求中传递oData过滤器,它会被很好地处理,这一切都可以正常工作。

但现在,我想使用FindOrganizationsQuery类而不是将ODataQueryOptions类型传递给Get操作,如下:

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

然而,查询参数始终为空。如果ODataQueryOptions参数在另一个类中,我该如何传递oData过滤器?

什么是 FindOrganizationsQuery,为什么需要它?为什么在 FindOrganizations 返回 IQueryable<OrganizationViewModel> 的同时需要内部的 ODataQueryOptions<Organization> - Aron
我使用CQS,因此我的应用程序层中有需要从服务层内部执行的查询和命令。我返回视图模型而不是实际组织,使用投影来决定要公开哪些属性(例如,我不公开导航属性)。但这一切都有效运行。 - L-Four
好的...但是为什么下游需要指定对Organization的查询并接收OrganizationViewModel呢?你仍然会像这样暴露导航属性... - Aron
完全不一样。视图模型与实际数据实体不同。 - L-Four
我猜查询只会在Organization和OrganizationViewModel中使用相同的属性名称才能起作用? - niklr
查询是在实际数据实体(ODataQueryOptions<Patient>)上完成的,结果是从该数据实体映射而来的自定义视图模型(request.ODataQuery.ApplyTo(_mgpQueryContext.Patients).Cast<Prim.Mgp.Application.QueryDtos.Patient>();)。 - L-Four
1个回答

0

您可以为FindOrganizationsQuery编写自定义参数绑定属性,{{link1:与我们为ODataQueryOptions所做的方式相同},然后使用该属性对FindOrganizationsQuery进行标注。

以下是一些示例代码:

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);
        }
    }
}

而且我复制了从Web API源代码中的扩展方法,因为它不是公共的。

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;
    }
}

我在这里提供完整的示例链接


谢谢。FindOrganizationsQuery被用在CustomQueryBinding中,那么如何将其通用化,使其适用于任何类型的类,而不仅仅是Organization/FindOrganizationsQuery? - L-Four
该参数类型在绑定中可用。您需要确定您在自定义查询的构造函数中期望的ODataQueryOptions<T>的T类型。我已经更新了示例代码,假设所有您的自定义查询都具有名为ODataQuery的公共属性。 此外,您需要在所有自定义查询上放置此属性。您也可以将此属性放在基类上,它会被继承。 - RaghuRam Nadiminti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接