请考虑以下使用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中解决该问题并发布正式版本.