c# – WebApi OData v3 OperationDescriptor根据格式返回不同的Title / Target URI(atom vs json)

请考虑以下使用OData v3的简单ASP.NET Web Api.

MyEntity.cs

public class MyEntity
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

MyEntitiesController.cs

public class MyEntitiesController : ODataController
{
    public IEnumerable<MyEntity> Get()
    {
        return new MyEntity[] { new MyEntity() { Id = Guid.NewGuid(), Name = "Name" } };
    }

    [HttpPost]
    public string MyAction()
    {
        return "Hello World!";
    }
}

WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var modelBuilder = new ODataConventionModelBuilder();
        modelBuilder.Namespace = "MyNamespace";
        modelBuilder.ContainerName = "MyContainer";
        modelBuilder.EntitySet<MyEntity>("MyEntities");

        var action = modelBuilder.Entity<MyEntity>().Action("MyAction");
        action.Returns<string>();

        foreach (var structuralType in modelBuilder.StructuralTypes)
        {
            // Resets the namespace so that the service contains only 1 namespace.
            structuralType.GetType().GetProperty("Namespace").SetValue(structuralType, "MyNamespace");
        }

        var model = modelBuilder.GetEdmModel();
        config.Routes.MapODataServiceRoute("OData", "odata", model);
    }
}

在客户端,我添加了一个简单的服务参考.

Program.cs中

class Program
{
    static void Main(string[] args)
    {
        var contextAtom = new MyContainer(new Uri("http://localhost:63939/odata/"));
        contextAtom.Format.UseAtom();
        var myEntityAtom = contextAtom.MyEntities.First();

        // Outputs: http://localhost:63939/odata/MyEntities(guid'2c2431cd-4afa-422b-805b-8398b9a29fec')/MyAction
        var uriAtom = contextAtom.GetEntityDescriptor(myEntityAtom).OperationDescriptors.First().Target;
        Console.WriteLine(uriAtom);

        // Works fine using ATOM format!
        var responseAtom = contextAtom.Execute<string>(uriAtom, "POST", true);

        var contextJson = new MyContainer(new Uri("http://localhost:63939/odata/"));
        contextJson.Format.UseJson();
        var myEntityJson = contextJson.MyEntities.First();

        // Outputs: http://localhost:63939/odata/MyEntities(guid'f31a8332-025b-4dc9-9bd1-27437ae7966a')/MyContainer.MyAction
        var uriJson = contextJson.GetEntityDescriptor(myEntityJson).OperationDescriptors.First().Target;
        Console.WriteLine(uriJson);

        // Throws an exception using the JSON uri in JSON format!
        var responseJson = contextJson.Execute<string>(uriJson, "POST", true);

        // Works fine using ATOM uri in JSON format!
        var responseJson2 = contextJson.Execute<string>(uriAtom, "POST", true);
    }
}

我的问题是,根据用于查询实体的格式,操作描述符目标URI是不同的.来自ATOM的目标URI工作正常,但来自JSON的目标URI总是抛出异常.

而不是手动连接URI,有没有办法让操作描述符在使用两种格式(ATOM和JSON)时工作?

请注意,我遇到了与OData v4相同的问题,但是将MyNamespace.MyAction作为标题和目标URI而不是MyContainer.MyAction.

最佳答案 我能够重现这个问题,并且是客户端的一个错误.我找到的唯一解决方法是扩展MyContainer类以提供调用操作的强类型方法:

namespace <NAMESPACE_OF_MYCONTAINER_CLASS>
{
    public partial class MyContainer
    {
        public double MyAction(Guid id)
        {
            Uri actionUri = new Uri(this.BaseUri,
                String.Format("MyEntities(guid'{0}')/MyAction", id)
                );

            return this.Execute<string>(actionUri, 
                "POST", true).First();
        }
    }
} 

正如here所描述的那样.我已经找到了这个问题而且看起来很老了,我发现这是post,当一个人(Uffe Lauesen)在json格式为jas格式时读取ActionDescriptor类的Title(而不是Target)属性时,他在第二篇文章中解释了一些额外的行为.用过的.

你仍然在他们的github page.中打开odata.net的问题

更新:

我跟踪了这​​个问题,Atom格式使用NoOpEntityMetadataBuilder返回非计算操作(使用原子格式解析xml并从feed获取操作).

internal override IEnumerable<ODataAction> GetActions()
{
    DebugUtils.CheckNoExternalCallers();
    return this.entry.NonComputedActions;
}

相反,Json格式使用ODataConventionalEntityMetadataBuilder,它返回与非计算操作连接的计算操作:

internal override IEnumerable<ODataAction> GetActions()
{
    DebugUtils.CheckNoExternalCallers();
    return ODataUtilsInternal.ConcatEnumerables(this.entryMetadataContext.Entry.NonComputedActions, this.MissingOperationGenerator.GetComputedActions());
}

对于计算操作,我们在EdmLibraryExtensions中调用此扩展函数:

internal static string FullName(this IEdmEntityContainerElement containerElement)
{
    Debug.Assert(containerElement != null, "containerElement != null");

    return containerElement.Container.Name + "." + containerElement.Name;
}

所以我相信这里更好的是没有返回container.Name,只有containerElement.Name.使用此最小更改运行修补版本的Microsoft OData Library可以避免此问题,直到在github中解决该问题并发布正式版本.

点赞