1

从 Atom 更改为 JSON (Light) 后,我遇到了一个严重的问题。我们使用 WCF 数据服务 5.6 并将其配置为使用 json 以减少通过网络传输的数据量...

{"odata.error":{"code":"","message":{"lang":"de-AT","value":"An error occurred while     processing this request."},
"innererror":{"message":"Multiple annotations with the name 'odata.bind' were detected for the property with name 'Employees'. In OData, duplicate annotations are not allowed.","type":"Microsoft.Data.OData.ODataException","stacktrace":"  
 at Microsoft.Data.OData.DuplicatePropertyNamesChecker.AddODataPropertyAnnotation(String propertyName, String annotationName, Object annotationValue)\r\n   
 at Microsoft.Data.OData.JsonLight.ODataJsonLightDeserializer.ProcessPropertyAnnotation(String annotatedPropertyName, String annotationName, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func`2 readPropertyAnnotationValue)\r\n   
 at Microsoft.Data.OData.JsonLight.ODataJsonLightDeserializer.ParseProperty(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func`2 readPropertyAnnotationValue, String& parsedPropertyName)\r\n   
at Microsoft.Data.OData.JsonLight.ODataJsonLightDeserializer.ProcessProperty(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func`2 readPropertyAnnotationValue, Action`2 handleProperty)\r\n   
 at Microsoft.Data.OData.JsonLight.ODataJsonLightEntryAndFeedDeserializer.ReadEntryContent(IODataJsonLightReaderEntryState entryState)\r\n   
 at Microsoft.Data.OData.JsonLight.ODataJsonLightReader.ReadAtNavigationLinkEndImplementationSynchronously()\r\n   
 at Microsoft.Data.OData.JsonLight.ODataJsonLightReader.ReadAtNavigationLinkEndImplementation()\r\n   
at Microsoft.Data.OData.ODataReaderCore.ReadImplementation()\r\n   
 at Microsoft.Data.OData.ODataReaderCore.ReadSynchronously()\r\n   
 at Microsoft.Data.OData.ODataReaderCore.InterceptException[T](Func`1 action)\r\n   
 at Microsoft.Data.OData.ODataReaderCore.Read()\r\n  
 at System.Data.Services.Serializers.EntityDeserializer.ReadEntry(ODataReader odataReader, SegmentInfo topLevelSegmentInfo)\r\n  
 at System.Data.Services.Serializers.EntityDeserializer.Read(SegmentInfo segmentInfo)\r\n  
 at System.Data.Services.Serializers.ODataMessageReaderDeserializer.Deserialize(SegmentInfo segmentInfo)"}}}

我们的客户端代码如下所示:

视图模型

//Insert control
var control = new Control
{
     Name = this.Name,
     ShortName = this.ShortName,
     ////etc...
};

this.ControlAgent.AddToContext(control);

foreach (var emp in this.EnforcingEmpColl.AddedItems)
{
     this.ControlAgent.AddEnforcingEmployee(control, emp);
}

this.ControlAgent.SaveControl(control, (s1, e1) => { ////etc.. });

控制代理

public class ControlServiceAgent : ServiceAgentBase<Control>, IControlServiceAgent
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ControlServiceAgent"/> class.
    /// </summary>
    public ControlServiceAgent()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ControlServiceAgent"/> class.
    /// </summary>
    /// <param name="uri">service uri</param>
    public ControlServiceAgent(Uri uri)
        : base(uri)
    {
    }

    /// <summary>
    /// Adds an Enforcing Emp to the Control
    /// </summary>
    /// <param name="control">control</param>
    /// <param name="emp">enforcing Employee</param>
    public void AddEnforcingEmployee(Control control, Employee emp)
    {
        this.Context.AttachTo("Employees", emp, "*");
        this.Context.AddLink(control, "Employees", emp);
    }

    /// <summary>
    /// Adds an control to the context
    /// </summary>
    /// <param name="entity">entity to add</param>
    public void AddToContext(Control entity)
    {
        this.Context.AddToControls(entity);
    }

    /// <summary>
    /// Adds and saves a control and notifies the tree
    /// </summary>
    /// <param name="entity">control to add</param>
    /// <param name="handler">callback</param>
    public void SaveControl(Control entity, EventHandler<ResponseEventArgs<Control>> handler)
    {   
        this.Context.BeginSaveChanges(SaveChangesOptions.ReplaceOnUpdate, 
            ar =>
                {
                    try
                    {
                        DataServiceResponse response = this.Context.EndSaveChanges(ar);
                        Control controlToAdd = null;

                        // Enumerate the returned responses.
                        foreach (ChangeOperationResponse change in response)
                        {
                            // Get the descriptor for the entity.
                            var descriptor = change.Descriptor as EntityDescriptor;

                            if (descriptor != null)
                            {
                                controlToAdd = descriptor.Entity as Control;
                            }
                        }

                        if (controlToAdd == null)
                        {
                            handler(this, new ResponseEventArgs<Control>("[SP-Control]: Error while SaveControl. Saving the added control failed!", null));
                        }
                        else
                        {
                            handler(this, new ResponseEventArgs<Control>(controlToAdd));
                        }
                    }
                    catch (Exception e)
                    {
                        handler(this, new ResponseEventArgs<Control>("[SP-Control]: Error while SaveControl. Saving the added control failed!", e));
                    }
                }, null);
    }
}

}

public abstract class ServiceAgentBase<T> : ServiceBase, IServiceAgentBase<T>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ServiceAgentBase&lt;T&gt;"/> class.
    /// </summary>
    protected ServiceAgentBase() : base()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ServiceAgentBase&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="uri">service uri</param>
    protected ServiceAgentBase(Uri uri) : base(uri)
    {
    }

    /// <summary>
    /// Attaches an entity to the service context
    /// </summary>
    /// <param name="entity">entity to attach</param>
    public virtual void Attach(T entity)
    {
        this.Context.Detach(entity);

        ////persist changes regardless of whether the underlying entity has changed
        this.Context.AttachTo(entity.GetType().Name + "s", entity, "*");
    }

    /// <summary>
    /// Deletes an entity
    /// </summary>
    /// <param name="entity">entity to delete</param>
    public virtual void Delete(T entity)
    {
        this.Context.DeleteObject(entity);
    }

    /// <summary>
    /// Updates an entity
    /// </summary>
    /// <param name="entity">entity to update</param>
    public virtual void Update(T entity)
    {
        this.Context.UpdateObject(entity);
    }
}

ServiceBase 中唯一值得注意的是我们创建了上下文并使用了 Json

 this.Context.Format.UseJson();

如果我注释掉这一行并改用 Atompub,一切正常……你们中有人有同样的问题吗?有什么解决方案吗?

谢谢!

编辑:

提琴手的痕迹

JSON:

POST http://localhost/MyService/MyDataService.svc/Controls HTTP/1.1
Accept: application/json;odata=minimalmetadata
DataServiceVersion: 3.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
Content-Type: application/json;odata=minimalmetadata

{"odata.type":"Ria.DevelopmentModel.Control","Processes@odata.bind":
["http://localhost/MyService/MyDataService.svc/Processes(3)"],
"Employees@odata.bind":["http://localhost/MyService/MyDataService.svc/Employees(2627)"],
"Employees@odata.bind":["http://localhost/MyService/MyDataService.svc/Employees(2628)"],
"Accuracy":false,"Activity":null,"ActualStatus":0,"Automation":null,////All the other properties...}

原子

POST http://localhost/MyService/MyDataService.svc/Controls HTTP/1.1
Accept: application/atom+xml,application/xml
Content-Type: application/atom+xml
DataServiceVersion: 1.0;NetFx
MaxDataServiceVersion: 3.0;NetFx

<?xml version="1.0" encoding="utf-8"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<category term="Ria.DevelopmentModel.Control" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Processes" type="application/atom+xml;type=feed" title="Processes" href="http://localhost/MyService/MyDataService.svc/Processes(3)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Employees" type="application/atom+xml;type=feed" title="Employees" href="http://localhost/MyService/MyDataService.svc/Employees(2627)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Employees" type="application/atom+xml;type=feed" title="Employees" href="http://localhost/MyService/MyDataService.svc/Employees(2628)" />
<id /><title /><updated>2013-09-18T09:20:07Z</updated><author><name /></author><content type="application/xml">
<m:properties><d:Accuracy m:type="Edm.Boolean">false</d:Accuracy><d:Activity m:null="true" /><d:ActualStatus m:type="Edm.Byte">0</d:ActualStatus><d:Automation m:type="Edm.Byte" m:null="true" /> ////All the other props....
4

1 回答 1

0

这确实是 WCF 数据服务客户端库中的一个错误。具体来说,如果您AddLink()在同一个请求中使用相同的值(在您的示例中为“Employees”)多次调用sourceProperty并且您使用的是 Json,则客户端库将生成不正确的有效负载。

这非常麻烦,但作为一种解决方法,直到修复可用,您可以尝试确保每次BeginSaveChanges()调用,AddLink()自上次调用BeginSaveChanges(). 例如,如果用户AddEnforcingEmployee()在单击保存控件之前多次调用,您可以在内部跟踪该链接列表,而不是AddLink()立即调用上下文。然后,当用户确实单击“保存”时,您可以在每次调用AddLink()BeginSaveChanes()重复AddEnforcingEmployee()调用。

或者,您可以继续使用 Atom 而不是 JSON。

抱歉,目前我没有更好的答案给你。当修复可用时,我将更新此响应。(感谢您报告此事!)

于 2013-10-12T15:31:45.947 回答