【问题标题】:Web Api OData Post on a navigation property导航属性上的 Web Api OData Post
【发布时间】:2014-04-29 12:50:06
【问题描述】:

我正在尝试将实体及其相关对象保留在 ICollection 中。错误消息是(服务器名称已删除):

System.Data.Services.Client.DataServiceRequestException: System.Data.Services.Client.DataServiceClientException:未找到与请求匹配的 HTTP 资源 URI 'Service.Facade.Reports/odata/Reports(8236)/LayoutData'。
没有找到路由约定来为带有模板“~/entityset/key/navigation”的 OData 路径选择操作。

一些测试客户端代码:

string uriString = "Service.Facade.Reports/odata/";
Uri uri = new Uri(uriString);
DataServiceContext context = new DataServiceContext(uri, DataServiceProtocolVersion.V3);
var layoutData = new ReportLayoutData { Data = new byte[] { 1, 2, 3 } };
var list = new List<ReportLayoutData>();
list.Add(layoutData);
var report = new ReportLayout { LayoutData = list, Name = "thing" };
context.AddObject("Reports", report);
context.AddRelatedObject(report, "LayoutData", list[0]); //Newly Added for Web API
DataServiceResponse response = context.SaveChanges();

如果不调用 AddRelatedObject,服务就可以添加报告。但是当我单步执行服务时,导航属性不会自动序列化,这就是我添加 AddRelatedObject 的原因。以前,相同的代码(没有 AddRelatedObject)与 WCF 数据服务一起工作,我现在正在尝试转换为 Web API。

在服务级别,我似乎需要添加一个路由约定,这是我认为的:

public static void Register(HttpConfiguration config)
{
    // Web API configuration and services
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<ReportLayout>("Reports");
    builder.EntitySet<ReportLayoutData>("ReportLayoutData");
    builder.EntitySet<DatabaseInstance>("DataSources");
    builder.EntitySet<DataSourceObject>("DataSourceObject");
    config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());

    // Web API routes
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

我将此添加到我的控制器中以支持导航属性上的 POST / PUT。

// POST PUT odata/Reports(5)/LayoutData
[AcceptVerbs("POST", "PUT")]
public async Task<IHttpActionResult> CreateLink([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    ReportLayout report = await db.Reports.FindAsync(key);
    if (report == null)
    {
        return NotFound();
    }

    switch (navigationProperty)
    {
        case "LayoutData":
            string layoutKey = GetKeyFromLinkUri<string>(link);
            ReportLayoutData reportLayout = await db.ReportsData.FindAsync(layoutKey);
            if (reportLayout == null)
            {
                return NotFound();
            }
            report.LayoutData = Enumerable.Range(0,0).Select(x=>reportLayout).ToList();
            await db.SaveChangesAsync();
            return StatusCode(HttpStatusCode.NoContent);

        default:
            return NotFound();
    }
}

// Helper method to extract the key from an OData link URI.
private TKey GetKeyFromLinkUri<TKey>(Uri link)
{
    TKey key = default(TKey);

    // Get the route that was used for this request.
    IHttpRoute route = Request.GetRouteData().Route;

    // Create an equivalent self-hosted route. 
    IHttpRoute newRoute = new HttpRoute(route.RouteTemplate,
        new HttpRouteValueDictionary(route.Defaults),
        new HttpRouteValueDictionary(route.Constraints),
        new HttpRouteValueDictionary(route.DataTokens), route.Handler);

    // Create a fake GET request for the link URI.
    var tmpRequest = new HttpRequestMessage(HttpMethod.Get, link);

    // Send this request through the routing process.
    var routeData = newRoute.GetRouteData(
        Request.GetConfiguration().VirtualPathRoot, tmpRequest);

    // If the GET request matches the route, use the path segments to find the key.
    if (routeData != null)
    {
        ODataPath path = tmpRequest.GetODataPath();
        var segment = path.Segments.OfType<KeyValuePathSegment>().FirstOrDefault();
        if (segment != null)
        {
            // Convert the segment into the key type.
            key = (TKey)ODataUriUtils.ConvertFromUriLiteral(
                segment.Value, ODataVersion.V3);
        }
    }
    return key;
}

使用的 POCO

[Serializable]
public partial class ReportLayout
{
public const string DefaultName = "Report...";
public const string DefaultGroup = "Uncategorized";

public ReportLayout()
{
    LayoutData = new List<ReportLayoutData>();
    Name = DefaultName;
    Group = DefaultGroup;
    UserDefinedQuery = false;
}

public virtual DatabaseInstance DatabaseInstance { get; set; }
public virtual ICollection<ReportLayoutData> LayoutData { get; set; }
public string Name { get; set; }
public string Group { get; set; }
public int ReportLayoutID { get; set; }
public string Query { get; set; }
public bool UserDefinedQuery { get; set; }

public void SetData(byte[] data)
{
    if (LayoutData.Count() > 0)
    {
        LayoutData.ElementAt(0).Data = data;
    }
    else
    {
        LayoutData.Add(new ReportLayoutData() {Data = data});
    }
}
}

[Serializable]
public class ReportLayoutData
{
    public byte[] Data { get; set; }
    public virtual ReportLayout ReportLayout { get; set; }
    public int ReportLayoutDataID { get; set; }
}

【问题讨论】:

    标签: c# asp.net-web-api odata


    【解决方案1】:

    事实证明我几乎是对的,但在服务器端犯了几个愚蠢的错误,客户端代码也需要修改。

    CreateLink 操作中的服务器端修复:

    • report.LayoutData = Enumerable.Range(0,0).Select(x=>reportLayout).ToList(); => report.LayoutData = Enumerable.Range(0,1).Select(x=>reportLayout).ToList(); 天哪!

    • string layoutKey = GetKeyFromLinkUri<string>(link); => int layoutKey = GetKeyFromLinkUri<int>(link);

    客户端代码也进行了更改,以推动 DataServiceContext 做正确的事情:

    context.AddObject("Reports", report);
    context.AddObject("ReportLayoutData", layoutData);
    context.AddLink(report, "LayoutData", layoutData);
    

    上面的 DataServicecontext 设置转化为调用:

     // POST odata/Reports
     public async Task<IHttpActionResult> Post(ReportLayout reportlayout)
    
     // POST PUT odata/Reports(5)/LayoutData
     [AcceptVerbs("POST", "PUT")]
     public async Task<IHttpActionResult> CreateLink([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
    

    【讨论】:

      猜你喜欢
      • 2013-12-25
      • 1970-01-01
      • 2017-05-25
      • 1970-01-01
      • 1970-01-01
      • 2014-09-20
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多