dealing with dynamic properties on oData client

血红的双手。 提交于 2019-12-25 12:12:22

问题


I have the following class on both server and client

public class Entity
{
    public string Id {get; set;}
    public string Name {get; set;}
    public Dictionary<string, object> DynamicProperties {get; set;}
}

As far as I have seen all the examples of open type describes about having dynamic properties on the server side, but the properties on the client needs to be explicitly declared.When I send a POST request from the client how do i send the dynamic properties ?. I can't declare all the dynamic properties on the client side. There are numerous properties and each object will contain different set of dynamic properties in the client side. These dynamic properties are stored in the DynamicProperties dictionary in the client side. How do I send the object of above entity class to the server side, so that server will interpret the contents of DynamicProperties dictionary as dynamic properties ?. Any help is appreciated.

===========================Follow-up for sam's answer=======================

    static void Main(string[] args1)
    {
        container.Customers.ToList();
        Customer newCustomer = new Customer();
        newCustomer.Id = 19;
        newCustomer.Properties = new Dictionary<string, object>
        {
            {"IntProp", 9},
            {"DateTimeOffsetProp", new DateTimeOffset(2015, 7, 16, 1, 2, 3, 4, TimeSpan.Zero)},
            {"blah","ha"}
        };
        try
        {
            addCustomer(newCustomer);
            container.AddToCustomers(newCustomer);
            container.SaveChanges();
        }
        catch (Exception)
        {

        }
        Customer newCustomer1 = new Customer();
        newCustomer1.Id = 20;
        newCustomer1.Properties = new Dictionary<string, object>
        {
            {"IntProp", 10},
            {"dir","north"}
        };
        addCustomer(newCustomer1);
        container.AddToCustomers(newCustomer1);
        container.SaveChanges();
        newCustomer1.Properties["dir"] = "south";
        container.UpdateObject(newCustomer1);
        container.SaveChanges();
        Console.ReadKey();
    }

    private static void addCustomer(Customer customer)
    {
        container.Configurations.RequestPipeline.OnEntryStarting(args =>
        {
            foreach (var property in customer.Properties)
            {
                args.Entry.AddProperties(new ODataProperty
                {
                    Name = property.Key,
                    Value = property.Value // for enum, complex type, should to create ODataEnumValue and ODataComplexValue.
                });
            }
        });
    }

I am getting an error stating Multiple properties with the name 'IntProp' were detected in an entry or a complex value. In OData, duplicate property names are not allowed. Also, I doubt if creating an action each time before sending an object like how I am doing now is a valid approach as I get lot of objects from a source and I send it to the server. If I create an action for each object then it might blow up the memory as oData client holds these actions in memory. How do I handle my scenario ?. Kindly help me.

Also, one more question if I comment the container.Customers.ToList() it fails stating that I am trying to add undeclared properties. Why is that ?


回答1:


If you are using OData Client Code Generator, you can use the partial class to define/retrieve/save the dyanmic properties.

For example, in your client side, you can define a partial class for your Entity

public partial class Entity
{
    // Dynamic property "Email"
    [global::Microsoft.OData.Client.OriginalNameAttribute("Email")]
    public string Email
    {
        get
        {
            return this._Email;
        }
        set
        {
            this.OnEmailChanging(value);
            this._Email = value;
            this.OnEmailChanged();
            this.OnPropertyChanged("Email");
        }
    }

    private string _Email;
    partial void OnEmailChanging(string value);
    partial void OnEmailChanged();
}

Then, you can use this to insert/retrieve/save the dynamic property "Email".

You can do like this:

Container container = new Container(new Uri("http://..."));
Entity entity = new Entity();
...
entity.Email = "xxxx";
container.AddToEntities(entity);
container.SaveChanges();

For similar implementation, you can refer to my sample project.

========== iteration 2 ================

For client Entity class with IDictionary<string,object>, I think the hook is what you're looking for.

For example, on client side:

public partial class Entity
{
    public IDictionary<string, object> Properties { get; set; }

    .....
}

It should work if you insert the following codes before

container.AddToEntities(entity);

For example:

Entity entity = new Entity();
...
entity.Properties = new Dictionary<string, object>
{
    {"IntProp", 9},
    {"DateTimeOffsetProp", new DateTimeOffset(2015, 7, 16, 1, 2, 3, 4, TimeSpan.Zero)}
};

container.Configurations.RequestPipeline.OnEntryStarting(args =>
{
    foreach (var property in entity.Properties)
    {
        args.Entry.AddProperties(new ODataProperty
        {
            Name = property.Key,
            Value = property.Value
        });
    }
});

container.AddToEntities(entity);
container.SaveChanges();

Where, AddProperties is an extension method. You can find it in my sample project and the latest commit

Besides, the hood method only works with OData Client V6.12 or above.

Hope it can help you.

========== iteration 3 ================

  1. First, you call the following method,

    container.Configurations.RequestPipeline.OnEntryStarting(...);

    It means to add an action which will be called in later execution. In your codes, you call it twice, So, there are two actions added. These two actions will be called one by one when execute to save your newCustomer1 That's, newCustomer1 will have newCustomer's dynamic properties (action 1), meanwhile, it will have its own dynamic properties (action 2). That's why you got the duplicate property name exception.

To resolve it, you can just to renew a Container. See my project's update.

  1. For container.Customers.ToList(), it seems an OData client issue.



回答2:


[Answering my own question : Another approach]

Extending Sam Xu's approach for iteration 2. We can do it as below. (For the sake of clarity let's assume the name of the class in question as Book)

public partial class Book
{
    public string ISBN {get; set;}
    public IDictionary<string, object> DynamicProperties { get; set; }
}

// This portion can be put in a function and can be invoked only once 
container.Configurations.RequestPipeline.OnEntryStarting(args =>
{
   if(args.Entity.GetType() == typeof(Book))
   {
      var book = args.Entity as Book
      foreach (var property in book.DynamicProperties)
      {
         args.Entry.AddProperties(new ODataProperty
         {
           Name = property.Key,
           Value = property.Value
         });
      }
    }
 });

AddProperties extension method implementation is provided in Sam Xu's implementation



来源:https://stackoverflow.com/questions/31393018/dealing-with-dynamic-properties-on-odata-client

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!