Open slnetgit opened 3 years ago
“"Fehler beim Verarbeiten des Anforderungsdatenstroms. Im Batchmodus kann nur bei Bindungs-/Trennvorgängen über Querverweise auf eine Ressource”
Error processing request stream. In batch mode, you can only use cross-references to a resource during binding / separating processes
@slnetgit Kindly share a metadata so that we can reproduce the issue.
Hallo support team, the attached file ist he Model of the usecase. It is reproducible with any model.
We found this in basedata scenario when the user adds data where the added data e.g. company is added to
a DataServiceCollection
This causes a link from company to related object in modified state. When the related object stays assigned, the added object can be saved ok. When the user decides to un – assign the related object, the link is not removed and stays modified with target null.
the exception occurs.
Mit freundlichen Grüßen / Best Regards
Sascha Link
Von: Christof Sprenger @.> Gesendet: Dienstag, 4. Mai 2021 18:28 An: OData/odata.net @.> Cc: Sascha Link @.>; Mention @.> Betreff: Re: [OData/odata.net] DataserviceCollection adding related objects (#2077)
Reopened #2077https://github.com/OData/odata.net/issues/2077.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/OData/odata.net/issues/2077#event-4684414745, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AHHBQL7TSPR6JPMAYY2G2NTTMAOBHANCNFSM44ATJ3NA.
Hey @slnetgit Can you use Fiddler and share the batch request that is created.
I have made some observations when looking into this issue. I have created a sample service and client based on the same logic as your example above.
Using the model classes below in an odata service:
public class Book
{
public int Id { get; set; }
public string Isbn { get; set; }
public string Title { get; set; }
public Author MainAuthor { get; set; }
public ICollection<Author> Authors { get; set; }
}
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
}
I created an odata client with the sample code below:
Container dsc = new Container(new Uri("http://serviceUri"));
DataServiceCollection<Book> books = new DataServiceCollection<Book>(dsc, "Books", null, null);
Author author = dsc.Authors.First();
Book newBook = new Book()
{
Title = "12 years a slave",
Year = 2000
};
books.Add(newBook);
newBook.MainAuthor = author;
newBook.MainAuthor = null;
dsc.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset );
This is the request that is created.
POST http://localhost:6143/odata/$batch HTTP/1.1
Host: localhost:6143
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: multipart/mixed
Accept-Charset: UTF-8
User-Agent: Microsoft.OData.Client/7.9.0
Connection: Keep-Alive
Content-Type: multipart/mixed; boundary=batch_93bc3b22-709b-4438-8e8b-942768d89169
Content-Length: 1175
--batch_93bc3b22-709b-4438-8e8b-942768d89169
Content-Type: multipart/mixed; boundary=changeset_c9cd06b5-9df0-4310-a510-8c9d9900c5ae
--changeset_c9cd06b5-9df0-4310-a510-8c9d9900c5ae
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
POST http://localhost.fiddler:6143/odata/Books HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft.OData.Client/7.9.0
{"@odata.type":"#BookLibService.Models.Book","Id":0,"Isbn":null,"Title":"12 years a slave","Year":2000}
--changeset_c9cd06b5-9df0-4310-a510-8c9d9900c5ae
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 5
DELETE $3/MainAuthor/$ref HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft.OData.Client/7.9.0
--changeset_c9cd06b5-9df0-4310-a510-8c9d9900c5ae--
--batch_93bc3b22-709b-4438-8e8b-942768d89169--
When you call newBook.MainAuthor = null;
a DELETE ref request is added to the batch. This will happen even when you don't have a call newBook.MainAuthor = author;
.
If you only have newBook.MainAuthor = author;
without newBook.MainAuthor = null;
The request should have odata.bind
POST http://localhost:6143/odata/$batch HTTP/1.1
Host: localhost:6143
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: multipart/mixed
Accept-Charset: UTF-8
User-Agent: Microsoft.OData.Client/7.9.0
Connection: Keep-Alive
Content-Type: multipart/mixed; boundary=batch_2cde0dc6-5358-40bc-9de4-10723f456301
Content-Length: 909
--batch_2cde0dc6-5358-40bc-9de4-10723f456301
Content-Type: multipart/mixed; boundary=changeset_78985de2-7f64-45fa-8af8-a000d35690e8
--changeset_78985de2-7f64-45fa-8af8-a000d35690e8
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
POST http://localhost.fiddler:6143/odata/Books HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft.OData.Client/7.9.0
{"@odata.type":"#BookLibService.Models.Book","Id":0,"Isbn":null,"Title":"12 years a slave","Year":2000,"MainAuthor@odata.bind":"http://localhost:6143/odata/Authors(1)"}
--changeset_78985de2-7f64-45fa-8af8-a000d35690e8--
--batch_2cde0dc6-5358-40bc-9de4-10723f456301--
Conclusion
When you creating an entity and it's navigation property is set to null e.g newBook.MainAuthor = null
, you create a DELETE ref request. You won't create a DELETE ref when updating or deleting an entity
testEntities c2 = new testEntities(new Uri("http://localhost:53739/WcfDemoService.svc"));
var g1 = c2.Gesellschaftsform.FirstOrDefault();
testModel.Gesellschaftsform gesellschaftsform = c2.Gesellschaftsform.Where(p => p.Beschreibung != g1.Beschreibung).FirstOrDefault();
System.Data.Services.Client.DataServiceCollection<testModel.Firma> coll = new System.Data.Services.Client.DataServiceCollection<testModel.Firma>(c2);
var neu = new testModel.Firma();
neu.Bezeichnung = "new company4";
neu.Kommentar = "Test";
coll.Add(neu);
neu.Gesellschaftsform = g1;
neu.GesellschaftsformId = g1.id;
neu.GesellschaftsformId = null;
neu.Gesellschaftsform = null;
c2.SaveChanges( System.Data.Services.Client.SaveChangesOptions.Batch);
produces in Fiddler:
GET http://localhost:53739/WcfDemoService.svc/Gesellschaftsform()?$top=1 HTTP/1.1 DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 3.0;NetFx Accept: application/atom+xml,application/xml Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services Host: localhost:53739 Connection: Keep-Alive
HTTP/1.1 200 OK Cache-Control: no-cache Content-Length: 1148 Content-Type: application/atom+xml;type=feed;charset=utf-8 Server: Microsoft-HTTPAPI/2.0 X-Content-Type-Options: nosniff DataServiceVersion: 1.0; Date: Fri, 16 Jul 2021 06:45:32 GMT
<?xml version="1.0" encoding="utf-8"?>
GET http://localhost:53739/WcfDemoService.svc/Gesellschaftsform()?$filter=Beschreibung%20ne%20'GmbH'&$top=1 HTTP/1.1 DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 3.0;NetFx Accept: application/atom+xml,application/xml Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services Host: localhost:53739
HTTP/1.1 200 OK Cache-Control: no-cache Content-Length: 1146 Content-Type: application/atom+xml;type=feed;charset=utf-8 Server: Microsoft-HTTPAPI/2.0 X-Content-Type-Options: nosniff DataServiceVersion: 1.0; Date: Fri, 16 Jul 2021 06:45:32 GMT
<?xml version="1.0" encoding="utf-8"?>
POST http://localhost:53739/WcfDemoService.svc/$batch HTTP/1.1 DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 3.0;NetFx Content-Type: multipart/mixed; boundary=batch_eb598119-7f36-4bb2-8db7-6b8b0bba76be Accept: multipart/mixed Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services Host: localhost:53739 Content-Length: 1636 Expect: 100-continue
--batch_eb598119-7f36-4bb2-8db7-6b8b0bba76be Content-Type: multipart/mixed; boundary=changeset_2906d6e6-0abd-4fdc-ad80-dc424bf16b03
--changeset_2906d6e6-0abd-4fdc-ad80-dc424bf16b03 Content-Type: application/http Content-Transfer-Encoding: binary
POST http://localhost:53739/WcfDemoService.svc/Firma HTTP/1.1 Content-ID: 3 Content-Type: application/atom+xml DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 3.0;NetFx Accept: application/atom+xml,application/xml Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services
<?xml version="1.0" encoding="utf-8"?>
DELETE $3/$links/Gesellschaftsform HTTP/1.1 Content-ID: 5 DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 3.0;NetFx Accept: application/atom+xml,application/xml Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services
--changeset_2906d6e6-0abd-4fdc-ad80-dc424bf16b03-- --batch_eb598119-7f36-4bb2-8db7-6b8b0bba76be--
HTTP/1.1 202 Accepted Cache-Control: no-cache Content-Length: 885 Content-Type: multipart/mixed; boundary=batchresponse_b5ac8ab7-9aac-42eb-9dc5-c8990bcb9bf3 Server: Microsoft-HTTPAPI/2.0 X-Content-Type-Options: nosniff DataServiceVersion: 1.0; Date: Fri, 16 Jul 2021 06:45:41 GMT
--batchresponse_b5ac8ab7-9aac-42eb-9dc5-c8990bcb9bf3 Content-Type: multipart/mixed; boundary=changesetresponse_51dfaed0-fe34-4ebf-9174-d4ef0a1e03af
--changesetresponse_51dfaed0-fe34-4ebf-9174-d4ef0a1e03af Content-Type: application/http Content-Transfer-Encoding: binary
HTTP/1.1 400 Bad Request Content-ID: 5 X-Content-Type-Options: nosniff DataServiceVersion: 1.0; Content-Type: application/xml;charset=utf-8
<?xml version="1.0" encoding="utf-8"?>
Thanks for the repro @slnetgit Can you share a repro using the v4 OData client?
The V4 repro uses the following code on the client Container c1 = new Container(new Uri("http://localhost/testrestier/"));
var g1 = c1.Gesellschaftsform.FirstOrDefault();
DataServiceCollection<Testrestier.Firma> coll = new DataServiceCollection<Testrestier.Firma>(c1);
var neu = new Testrestier.Firma();
neu.Bezeichnung = "new company1";
neu.Kommentar = "Test";
coll.Add(neu);
neu.Gesellschaftsform = g1;
neu.GesellschaftsformId = g1.Id;
neu.GesellschaftsformId=null;
neu.Gesellschaftsform = null;
c1.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset);
In Fiddler this produces the following output :
GET http://localhost/testrestier/Gesellschaftsform?$top=1 HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.9.0 Host: localhost Connection: Keep-Alive
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; odata.metadata=minimal; charset=utf-8 Expires: -1 Server: Microsoft-IIS/10.0 OData-Version: 4.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Mon, 26 Jul 2021 09:39:47 GMT Content-Length: 115
{"@odata.context":"http://localhost/testrestier/$metadata#Gesellschaftsform","value":[{"id":1,"Beschreibung":"GmbH"}]}
POST http://localhost/testrestier/$batch HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: multipart/mixed; boundary=batch_87d9c078-9ec3-4532-8dfb-c332986367d2 Accept: multipart/mixed Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.9.0 Host: localhost Content-Length: 1127 Expect: 100-continue
--batch_87d9c078-9ec3-4532-8dfb-c332986367d2 Content-Type: multipart/mixed; boundary=changeset_959af259-f6c6-4553-9ed5-68e4819700f4
--changeset_959af259-f6c6-4553-9ed5-68e4819700f4 Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 2
POST http://localhost/testrestier/Firma HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Content-Type: application/json;odata.metadata=minimal Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.9.0
{"@odata.type":"#testrestier.Firma","Bezeichnung":"new company1","CreationBy":null,"CreationDate":null,"GesellschaftsformId":null,"id":0,"Kommentar":"Test"} --changeset_959af259-f6c6-4553-9ed5-68e4819700f4 Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 4
DELETE $2/Gesellschaftsform/$ref HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft.OData.Client/7.9.0
--changeset_959af259-f6c6-4553-9ed5-68e4819700f4-- --batch_87d9c078-9ec3-4532-8dfb-c332986367d2--
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: multipart/mixed; boundary=batchresponse_47b3eb72-4f94-4b71-9d0b-8c7dd5c55423 Expires: -1 Server: Microsoft-IIS/10.0 OData-Version: 4.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Mon, 26 Jul 2021 09:39:48 GMT Content-Length: 640
--batchresponse_47b3eb72-4f94-4b71-9d0b-8c7dd5c55423 Content-Type: multipart/mixed; boundary=changesetresponse_62b6035b-ad24-4cde-ba9b-ca3a31639667
--changesetresponse_62b6035b-ad24-4cde-ba9b-ca3a31639667 Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 2
HTTP/1.1 500 Internal Server Error Content-Type: application/json; odata.metadata=minimal; charset=utf-8 OData-Version: 4.0
{"error":{"code":"","message":"Ung\u00fcltiger URI: Das URI-Format konnte nicht bestimmt werden."}} --changesetresponse_62b6035b-ad24-4cde-ba9b-ca3a31639667-- --batchresponse_47b3eb72-4f94-4b71-9d0b-8c7dd5c55423--
When adding a related Entity which is already in database to a new unsaved Entity in Dataservicecontext ( WCF Odata V3 and OData WebApi V4 - no difference ) there is a Problem when this entity is set to null again - for example during editing . I found this in our Wcf OData V3 Application and also in latest Client of V4
Assemblies affected
packages\Microsoft.OData.Client.7.8.3\lib\net45\Microsoft.OData.Client.dll Microsoft.Data.Services.Client.5.8.4
Reproduce steps
See above description
Expected result
Save changes without Exception
Actual result
Exception is thrown in 5.8.4 System.Data.Services.DataServiceException: "Fehler beim Verarbeiten des Anforderungsdatenstroms. Im Batchmodus kann nur bei Bindungs-/Trennvorgängen über Querverweise auf eine Ressource
in Odata V4 also an exception is thron on saving.
Additional detail
In my Opinion the problem seems to be caused by the link - object which is placed in the dataservicecontext by the DataServiceCollection. After Assigning the related object as null ( properties in entitymodel are configured as nullable) the link object stays modified in Dataservicecontext but with target null.