处理 oData 客户端上的动态属性

dealing with dynamic properties on oData client

我在服务器和客户端上都有以下 class

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

据我所知,所有开放类型的示例都描述了在服务器端具有动态属性,但客户端的属性需要明确 declared.When 我发送一个 POST来自客户端的请求我如何发送动态属性?。我无法在客户端声明所有动态属性。有许多属性,每个对象将在客户端包含不同的动态属性集。这些动态属性存储在客户端的 DynamicProperties 字典中。如何将上述实体 class 的对象发送到服务器端,以便服务器将 DynamicProperties 字典的内容解释为动态属性?。感谢任何帮助。

===========================跟进sam的回答======= ================

    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.
                });
            }
        });
    }

我收到一条错误消息,指出 在条目或复杂值中检测到名称为 'IntProp' 的多个属性。在 OData 中,不允许重复 属性 个名称。此外,我怀疑每次在发送对象之前创建一个动作是否像我现在所做的那样是一种有效的方法,因为我从源中获取了很多对象并将其发送到服务器。如果我为每个对象创建一个动作,那么它可能会破坏内存,因为 oData 客户端将这些动作保存在内存中。我该如何处理我的场景?请帮助我。

此外,还有一个问题,如果我评论 container.Customers.ToList() 它会失败,说明我正在尝试添加未声明的属性。这是为什么?

如果您正在使用 OData Client Code Generator,您可以使用 partial class 到 define/retrieve/save 的动态属性。

例如,在您的客户端,您可以为您的 Entity

定义部分 class
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();
}

然后,您可以使用它来 insert/retrieve/save 动态 属性 "Email".

你可以这样做:

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

类似的实现可以参考my sample project.

==========迭代2 ================

对于 Entity class 和 IDictionary<string,object> 的客户,我认为钩子就是您要找的东西。

例如客户端:

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

    .....
}

如果你在前面插入以下代码应该可以工作

container.AddToEntities(entity);

例如:

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();

其中,AddProperties为扩展方法。您可以在 my sample project 中找到它 和 the latest commit

此外,hood方法仅适用于OData Client V6.12或更高版本。

希望对您有所帮助。

==========迭代3 ================

  1. 首先,你调用下面的方法,

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

    意思是添加一个action,后面执行的时候会调用。在您的代码中,您调用了两次,因此,添加了 两个 操作。这两个action会在执行的时候一一调用来拯救你的newCustomer1 也就是说,newCustomer1 将具有 newCustomer 的动态属性(操作 1),同时,它将具有自己的动态属性(操作 2)。这就是为什么你得到重复的 属性 名称异常。

要解决它,您可以只更新一个Container。查看我的项目更新。

  1. 对于container.Customers.ToList(),这似乎是一个 OData 客户端问题。

[回答我自己的问题:另一种方法]

将 Sam Xu 的方法扩展到迭代 2。我们可以按如下方式进行。 (为清楚起见,我们假设所讨论的 class 的名称为 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扩展方法实现在 Sam Xu 的实现中提供