Search code examples
c#.netodata

odata v4 client code generator: how to do deep insert?


What I have is something like

var item1 = new Item { CODE = "ABC1", NAME = "A B C 1" };
var item2 = new Item { CODE = "ABC2", NAME = "A B C 2" };
var items = new DataServiceCollection<Item >{ item1, item2 };
var mt    = new MyType { CURRDATE = DateTime.Now.toString(), ITEMS = items };
_container.AddToMyType(mt);
var resp = _container.SaveChanges();
//...etc

is that right? it's complaining something about

"Unhandled Exception: System.InvalidOperationException: An item could not be added to the collection. When items in a DataServiceCollection are tracked by the DataServiceContext, new items cannot be added before items have been loaded into the collection."


Solution

  • As far as I know, OData v4 client does not support deep insert.

    One way to do this is to create an OData Action method and serialize the entity before sending to the OData Action method and send as a string, then deserialize it on the server side.

    Another option I have listed below will let you do "POST" with parent / child:

    /// <summary>
    /// Deep insert parent and child.
    /// </summary>
    /// <param name="parentEntityPluralName"></param>
    /// <param name="entity"></param>
    public TEntity SaveWithChildren<TEntity>(string parentEntityPluralName, TEntity entity) where TEntity : BaseEntityType
    {
        // need to serialize the entity so that we can send parent and child together
        string serializedEntity = Newtonsoft.Json.JsonConvert.SerializeObject(entity, new Newtonsoft.Json.JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore });
    
        // create a handler for the httpclient
        using (System.Net.Http.HttpClientHandler httpHandler = new System.Net.Http.HttpClientHandler())
        {
            // create the httpclient and add the handler
            using (System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient(httpHandler))
            {
                // setup the headers
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Prefer", @"odata.include-annotations=""*""");
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json;odata.metadata=minimal");
    
                // setup the content to send
                using (System.Net.Http.StringContent odataContent = new System.Net.Http.StringContent(serializedEntity))
                {
                    // setup the content type to json
                    odataContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
    
                    // post the data to the odata service
                    using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsync(this.BaseUri + parentEntityPluralName, odataContent).Result)
                    {
                        // get back any errors or content
                        string content = response.Content.ReadAsStringAsync().Result;
    
                        // show error if service failed
                        if (response.IsSuccessStatusCode == false)
                        {
                            throw new Exception(content);
                        }
    
                        // try to convert the object back from the service call
                        return Newtonsoft.Json.JsonConvert.DeserializeObject<TEntity>(content);
                    }
                }
            }
        }
    }
    

    Place this method in a new class that is partial to your "proxy" generated class.
    Then call your Container class and call the SaveWithChildren method.
    This will call you parent controllers POST method.
    It ONLY calls the parent controller POST method and you will have to iterate the children on the server.