C# 用于';铸造';
为以下查询使用base Get方法构建ODataController:C# 用于';铸造';,c#,asp.net-web-api,odata,C#,Asp.net Web Api,Odata,为以下查询使用base Get方法构建ODataController: http://localhost:8080/api/Bases 这很简单: [EnableQuery] public IHttpActionResult Get() { return Ok(new List<Base>()); } 我可以在同一控制器中定义以下方法: [EnableQuery] public IHttpActionResult GetFromDerivedA() { retur
http://localhost:8080/api/Bases
这很简单:
[EnableQuery]
public IHttpActionResult Get()
{
return Ok(new List<Base>());
}
我可以在同一控制器中定义以下方法:
[EnableQuery]
public IHttpActionResult GetFromDerivedA()
{
return Ok(new List<DerivedA>());
}
我正在使用:
- Microsoft.AspNet.WebApi 5.2.3
- Microsoft.OData 6.13.0
- Microsoft.AspNet.OData 5.6.0
更新 我可以创建并让重写的SelectAction返回我的泛型方法,但似乎我必须忘记泛型方法方法: 无法在控制器“MyProject.Controllers.BasesController”上调用操作方法“System.Web.Http.IHttpActionResult GetFrom[T]()” 因为action方法是泛型方法。“ 那么这个怎么样,这可能吗
[EnableQuery]
public IHttpActionResult GetFrom(Type derivedType)
{
//snip!
}
如果没有,还有其他想法吗?这里有一种方法,我可以通过一些思考来实现这一点。这是一个相当长的路,但最终的控制器方法是如此简单,它是值得的 首先,创建一个新的路由约定。请注意,我们将把所有cast请求转发到名为
GetFrom
的方法:
public class CastRoutingConvention : EntitySetRoutingConvention
{
public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
{
if (odataPath.PathTemplate == "~/entityset/cast")
{
HttpMethod httpMethod = controllerContext.Request.Method;
var collectionType = (IEdmCollectionType)odataPath.EdmType;
var entityType = (IEdmEntityType)collectionType.ElementType.Definition;
var type = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic)
.SelectMany(a => a.DefinedTypes)
.FirstOrDefault(t => t.FullName == entityType.FullTypeName());
controllerContext.RouteData.Values["type"] = type;
if (httpMethod == HttpMethod.Get)
return "GetFrom";
else if (httpMethod == HttpMethod.Post)
return "PostFrom";
else
return base.SelectAction(odataPath, controllerContext, actionMap);
}
else
return base.SelectAction(odataPath, controllerContext, actionMap);
}
}
现在,由于默认模型绑定器不会从路由数据字典中读取任意参数名称,因此我们需要路由数据:
using System;
using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;
namespace Example
{
public class RouteDataModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
object model;
if (!actionContext.RequestContext.RouteData.Values.TryGetValue(bindingContext.ModelName, out model))
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"No route data named '{bindingContext.ModelName}'.");
return false;
}
else if (!bindingContext.ModelType.IsAssignableFrom(model.GetType()))
{
try
{
model = Convert.ChangeType(model, bindingContext.ModelType);
}
catch
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"Route data cannot be converted to type '{bindingContext.ModelType.FullName}'.");
return false;
}
}
bindingContext.Model = model;
return true;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
public class RouteDataAttribute : ModelBinderAttribute
{
public RouteDataAttribute()
{
this.BinderType = typeof(RouteDataModelBinder);
}
}
}
最后,在控制器中添加所需的方法。请注意它是多么微不足道:
[EnableQuery]
public IHttpActionResult GetFrom([RouteData]Type type)
{
var ofType = typeof(Queryable).GetMethod("OfType").MakeGenericMethod(type);
return Ok((IQueryable<Base>)ofType.Invoke(null, new object[] { this.Context.Bases }));
}
建议:在
SelectAction
中执行CastPathSegment
->CLR类型解析,并将类型存储在controllerContext.RoutedData.Values
上。然后,在控制器操作中,您可以从路由数据中获取类型,或者按照最初的意图将派生类型声明为参数。好主意!我就快到了,但我不知道如何添加参数:/I如果我这样做:controllerContext.RouteData.Values[“type”]=type
控制器方法是:GetFrom([FromODataUri]类型类型)
Type始终为Null我从ODatauri中删除时获得一个值,并将类型更改为string:GetFrom(string类型)
,但这完全违背了目的!您的类型存储在路由数据中,因此不要使用[FromUri]
或[FromODataUri]
。但是,事实证明,默认模型绑定器不会从路由数据字典中读取任意参数名称。您将需要一个新的解决方案。现在您可以编写GetFrom([RouteData]Type)
。
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var builder = new ODataConventionModelBuilder() { Namespace = "Default" };
builder.DataServiceVersion = Version.Parse("4.0");
//snip! entity configuration
var conventions = ODataRoutingConventions.CreateDefault();
conventions.Insert(0, new CastRoutingConvention());
config.MapODataServiceRoute(
routeName:"ODataRoute",
routePrefix: "api",
routingConventions: conventions,
pathHandler: new DefaultODataPathHandler(),
model: builder.GetEdmModel());
}
using System;
using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;
namespace Example
{
public class RouteDataModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
object model;
if (!actionContext.RequestContext.RouteData.Values.TryGetValue(bindingContext.ModelName, out model))
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"No route data named '{bindingContext.ModelName}'.");
return false;
}
else if (!bindingContext.ModelType.IsAssignableFrom(model.GetType()))
{
try
{
model = Convert.ChangeType(model, bindingContext.ModelType);
}
catch
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"Route data cannot be converted to type '{bindingContext.ModelType.FullName}'.");
return false;
}
}
bindingContext.Model = model;
return true;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
public class RouteDataAttribute : ModelBinderAttribute
{
public RouteDataAttribute()
{
this.BinderType = typeof(RouteDataModelBinder);
}
}
}
[EnableQuery]
public IHttpActionResult GetFrom([RouteData]Type type)
{
var ofType = typeof(Queryable).GetMethod("OfType").MakeGenericMethod(type);
return Ok((IQueryable<Base>)ofType.Invoke(null, new object[] { this.Context.Bases }));
}
return Ok(inRamEntities.Where(e => e.GetType() == type));