OData客户端请求管道无法在Odata V4中运行

时间:2015-03-27 18:00:36

标签: odata asp.net-web-api

我正在关注下面的示例博客,以删除并添加请求ODataEntry类中的属性。

http://blogs.msdn.com/b/odatateam/archive/2013/07/26/using-the-new-client-hooks-in-wcf-data-services-client.aspx

但即使代码工作正常并在我放置断点时正确添加和删除属性,所有实体属性都会转到服务器未更改。

唯一区别我看到这个我正在使用OData V4和新的Ondata客户端来连接。

我的代码如下所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Client.Default;

namespace Client
{

using Client.MvcApplication1.Models;

using Microsoft.OData.Core;

internal class Program
{
    private static void Main(string[] args)
    {
        Container container = new Container(new Uri("http://localhost:55000/api/"));

        container.Configurations.RequestPipeline.OnEntryEnding(
            w =>
            {
                w.Entry.RemoveProperties("Name");
            });

        Test test = new Test();
        test.Name = "Foo";
        CustomFields cs = new CustomFields { ServiceId = 3 };
        cs.Foo1 = 2;
        test.S_1 = cs;
        container.AddToTests(test);
        container.SaveChanges();
    }
}

public static class Extensions
{

    public static void RemoveProperties(this ODataEntry entry, params string[] propertyNames)
    {

        var properties = entry.Properties as List<ODataProperty>;
        if (properties == null)
        {
            properties = new List<ODataProperty>(entry.Properties);
        }

        var propertiesToRemove = properties.Where(p => propertyNames.Any(rp => rp == p.Name));
        foreach (var propertyToRemove in propertiesToRemove.ToArray())
        {
            properties.Remove(propertyToRemove);
        }

        entry.Properties = properties;

    }

    public static void AddProperties(this ODataEntry entry, params ODataProperty[] newProperties)
    {
        var properties = entry.Properties as List<ODataProperty>;
        if (properties == null)
        {
            properties = new List<ODataProperty>(entry.Properties);
        }

        properties.AddRange(newProperties);

        entry.Properties = properties;

    }
}
}

如果我改变并开始收听RequestPipeline.OnEntryStarting,我会收到验证错误,即未在拥有实体中定义新属性。但是根据Microsoft.OData.CLient的代码,这个错误不应该发生,因为检查IEdmStructuredType.IsOpen但仍然发生错误。因此,如何计算owningStructuredType的问题似乎很深。在我的容器上,我确实看到了正确的edm模型,其实体标记为IsOpen = true。

应该通过但是失败的Odata lib代码

internal static IEdmProperty ValidatePropertyDefined(string propertyName, IEdmStructuredType owningStructuredType)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)");

            if (owningStructuredType == null)
            {
                return null;
            }

            IEdmProperty property = owningStructuredType.FindProperty(propertyName);

            // verify that the property is declared if the type is not an open type.
            if (!owningStructuredType.IsOpen && property == null)
            {
                throw new ODataException(Strings.ValidationUtils_PropertyDoesNotExistOnType(propertyName, owningStructuredType.ODataFullName()));
            }

            return property;
        }

客户代码:

container.Configurations.RequestPipeline.OnEntryStarting(
                w =>
                {
                    w.Entry.RemoveProperties("Name");
                    w.Entry.AddProperties(new ODataProperty
                                              {
                                                  Name = "NewProperty",
                                                  Value = 1
                                              });
                });

错误:

The property 'NewProperty' does not exist on type 'Client.MvcApplication1.Models.Test'. Make sure to only use property names that are defined by the type.

   at Microsoft.OData.Core.WriterValidationUtils.ValidatePropertyDefined(String propertyName, IEdmStructuredType owningStructuredType)
   at Microsoft.OData.Core.JsonLight.ODataJsonLightPropertySerializer.WriteProperty(ODataProperty property, IEdmStructuredType owningType, Boolean isTopLevel, Boolean allowStreamProperty, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
   at Microsoft.OData.Core.JsonLight.ODataJsonLightPropertySerializer.WriteProperties(IEdmStructuredType owningType, IEnumerable`1 properties, Boolean isComplexValue, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
   at Microsoft.OData.Core.JsonLight.ODataJsonLightWriter.StartEntry(ODataEntry entry)
   at Microsoft.OData.Core.ODataWriterCore.<>c__DisplayClass14.<WriteStartEntryImplementation>b__12()
   at Microsoft.OData.Core.ODataWriterCore.InterceptException(Action action)
   at Microsoft.OData.Core.ODataWriterCore.WriteStartEntryImplementation(ODataEntry entry)
   at Microsoft.OData.Core.ODataWriterCore.WriteStart(ODataEntry entry)
   at Microsoft.OData.Client.ODataWriterWrapper.WriteStart(ODataEntry entry, Object entity)
   at Microsoft.OData.Client.Serializer.WriteEntry(EntityDescriptor entityDescriptor, IEnumerable`1 relatedLinks, ODataRequestMessageWrapper requestMessage)
   at Microsoft.OData.Client.BaseSaveResult.CreateRequestData(EntityDescriptor entityDescriptor, ODataRequestMessageWrapper requestMessage)
   at Microsoft.OData.Client.BaseSaveResult.CreateChangeData(Int32 index, ODataRequestMessageWrapper requestMessage)
   at Microsoft.OData.Client.SaveResult.CreateNonBatchChangeData(Int32 index, ODataRequestMessageWrapper requestMessage)
   at Microsoft.OData.Client.SaveResult.CreateNextChange()

1 个答案:

答案 0 :(得分:0)

我使用客户端上定义的部分类来添加我需要的额外属性。这允许我在其中放置任何逻辑以及具有属性更改通知。我使用以下扩展方法来删除这些属性。我想我实际上已经从您链接的文章中获得了原始代码。

public static class DbContextExtensions
{
    public static void RemoveProperties(this ODataEntry entry, params string[] propertyNames)
    {
        var properties = entry.Properties as List<ODataProperty>;
        if (properties == null)
        {
            properties = new List<ODataProperty>(entry.Properties);
        }
        var propertiesToRemove = properties.Where(p => propertyNames.Any(rp => rp == p.Name));
        foreach (var propertyToRemove in propertiesToRemove.ToArray())
        {
            properties.Remove(propertyToRemove);
        }
        entry.Properties = properties;
    }

    public static DataServiceClientResponsePipelineConfiguration RemoveProperties<T>(this DataServiceClientResponsePipelineConfiguration responsePipeline, Func<string, Type> resolveType, params string[] propertiesToRemove)
    {
        return responsePipeline.OnEntryEnded((args) =>
        {
            Type resolvedType = resolveType(args.Entry.TypeName);
            if (resolvedType != null && typeof(T).IsAssignableFrom(resolvedType))
            {
                args.Entry.RemoveProperties(propertiesToRemove);
            }
        });
    }

    public static DataServiceClientRequestPipelineConfiguration RemoveProperties<T>(this DataServiceClientRequestPipelineConfiguration requestPipeline, params string[] propertiesToRemove)
    {
        return requestPipeline.OnEntryStarting((args) =>
        {
            if (typeof(T).IsAssignableFrom(args.Entity.GetType()))
            {
                args.Entry.RemoveProperties(propertiesToRemove);
            }
        });
    }
}

请注意,在下面的方法中,它挂起OnEntryStarted。文章中的代码挂钩了OnEntryEnded,它一度为我工作,然后在我更新到更新版本的ODataClient时破坏了。 OnEntryStarted是这种方法的一种方式。

public static DataServiceClientRequestPipelineConfiguration RemoveProperties<T>(this DataServiceClientRequestPipelineConfiguration requestPipeline, params string[] propertiesToRemove)
    {
        return requestPipeline.OnEntryStarting((args) =>
        {
            if (typeof(T).IsAssignableFrom(args.Entity.GetType()))
            {
                args.Entry.RemoveProperties(propertiesToRemove);
            }
        });
    }

我还为Container创建了一个分部类,并实现了部分方法OnContextCreated。这是您使用扩展方法删除不会发送到服务器的属性的地方。

  partial void OnContextCreated()
    {
        Configurations.RequestPipeline.RemoveProperties<Customer>(new string[] { "FullName", "VersionDetails" });
        Configurations.RequestPipeline.RemoveProperties<SomeOtherType>(new string[] { "IsChecked", "IsReady" });
    }

确保您的部分类和DBContextExtensions类与我们的容器位于同一名称空间中,一切都应该正常工作。

希望有所帮助。