2

我正在开发一个更大的项目,该项目使用 WCF 数据服务和 OData 在前端和后端之间进行通信,并且在创建实体时我们遇到了性能问题。深入研究该问题后发现,当客户端尝试保存新实体时,会在服务中执行查询,返回所有实体,然后将其丢弃,然后插入数据。

我在想这个问题与我们的应用程序是如何创建的有关。它使用自定义提供程序,但奇怪的是即使是最简单的测试项目也会出现问题。

我使用了下面发布的以下代码,并在 Provider.cs 中设置了断点。当我使用 HttpRequester Firefox 插件调用它以根据 OData 发送 POST 请求(插入)时

  1. IQueryable 被调用
  2. 调用 CreateResource
  3. SetValue 被多次调用
  4. 调用 SaveChanges

我有一个问题,为什么调用 IQueryable 以及如何防止它?我无法弄清楚。

在我们的现实生活场景中,而不是在这个测试应用程序中,IQueryable 返回数千条记录,甚至更多,并且可能非常耗时。这会影响插入操作的性能。

请求插入记录

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xml:base="http://localhost:50366/MyDataService.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<id>http://localhost:50366/MyDataService.svc/Employees(0)</id>
<title type="text"></title>
<updated>2012-07-31T18:03:45Z</updated>
<author>
  <name />
</author>
<link rel="edit" title="Employee" href="Employees(0)" />
<category term="Test.Dto.Employee" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
  <m:properties>
    <d:EmployeeID m:type="Edm.Int32">2</d:EmployeeID>
    <d:LastName>Test</d:LastName>
    <d:FirstName>Data</d:FirstName>
  </m:properties>
</content>
</entry>

MyDataService.svc.cs:

using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel.Web;
using System.Web;

namespace Test
{
    public class MyDataService : DataService<Provider>
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        }
    }
}

员工.cs

using System.Data.Services.Common;

namespace Test.Dto
{

    [DataServiceKey("EmployeeID")]
    public class Employee
    {
        public virtual int EmployeeID { set; get; }
        public virtual string LastName { set; get; }
        public virtual string FirstName { set; get; }
    }

}

提供者.cs

using System.Linq;
using Test.Dto;
using System.Collections.Generic;
using System.Data.Services;
namespace Test
{

    public class Provider : IUpdatable
    {
        static IList<Employee> _employees = new List<Employee>() {
            new Employee {
                EmployeeID = 1,
                FirstName = "No",
                LastName = "Name"
            }
        };

        IList<Employee> _updates = new List<Employee>();

        public IQueryable<Employee> Employees 
        { 
            get 
            {
                return Provider._employees.AsQueryable();
            } 
        }

        public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
        {
            throw new System.NotImplementedException();
        }

        public void ClearChanges()
        {
            _updates.Clear();
        }

        public object CreateResource(string containerName, string fullTypeName)
        {
            if (Equals(fullTypeName, typeof(Employee).FullName))
            {
                var entity = new Employee();
                _updates.Add(entity);
                return entity;
            }
            else
            {
                throw new System.NotImplementedException();
            }                              
        }

        public void DeleteResource(object targetResource)
        {
            throw new System.NotImplementedException();
        }

        public object GetResource(IQueryable query, string fullTypeName)
        {
            throw new System.NotImplementedException();
        }

        public object GetValue(object targetResource, string propertyName)
        {
            throw new System.NotImplementedException();
        }

        public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
        {
            throw new System.NotImplementedException();
        }

        public object ResetResource(object resource)
        {
            throw new System.NotImplementedException();
        }

        public object ResolveResource(object resource)
        {
            return resource;
        }

        public void SaveChanges()
        {
            foreach (var item in _updates)
            {
                _employees.Add(item);                
            }
        }

        public void SetReference(object targetResource, string propertyName, object propertyValue)
        {
            throw new System.NotImplementedException();
        }

        public void SetValue(object targetResource, string propertyName, object propertyValue)
        {
            targetResource.GetType().GetProperty(propertyName).SetValue(targetResource, propertyValue, null);
        }
    }

}

中断 IQueryable 时的堆栈跟踪

>    WebApplication5.DLL!Test.Provider.Employees.get() Line 24    C#
    [Lightweight Function]    
    System.Data.Services.dll!System.Data.Services.Providers.ReflectionServiceProvider.GetResourceContainerInstance(System.Data.Services.Providers.ResourceSet resourceContainer) + 0x1ec bytes    
    System.Data.Services.dll!System.Data.Services.Providers.BaseServiceProvider.GetQueryRootForResourceSet(System.Data.Services.Providers.ResourceSet container) + 0xb bytes    
    System.Data.Services.dll!System.Data.Services.RequestUriProcessor.CreateFirstSegment(System.Data.Services.IDataService service, string identifier, bool checkRights, string queryPortion, bool isLastSegment, out bool crossReferencingUrl) + 0x40e bytes    
    System.Data.Services.dll!System.Data.Services.RequestUriProcessor.CreateSegments(string[] segments, System.Data.Services.IDataService service) + 0x103 bytes    
    System.Data.Services.dll!System.Data.Services.RequestUriProcessor.ProcessRequestUri(System.Uri absoluteRequestUri, System.Data.Services.IDataService service) + 0x3b bytes    
    System.Data.Services.dll!System.Data.Services.DataService<Test.Provider>.ProcessIncomingRequestUri() + 0xe2 bytes    
    System.Data.Services.dll!System.Data.Services.DataService<Test.Provider>.HandleRequest() + 0xc0 bytes    
    System.Data.Services.dll!System.Data.Services.DataService<Test.Provider>.ProcessRequestForMessage(System.IO.Stream messageBody) + 0x65 bytes    
    [Lightweight Function]    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(object instance, object[] inputs, out object[] outputs) + 0x33f bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x137 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x5e bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x6c bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x89 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x59 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x3b bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x4e bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x125 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x34 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(bool isOperationContextSet) + 0xff bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext request, bool cleanThread, System.ServiceModel.OperationContext currentOperationContext) + 0x44b bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext request, System.ServiceModel.OperationContext currentOperationContext) + 0x127 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult result) + 0x43 bytes    
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(System.IAsyncResult result) + 0x44 bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x32 bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously) + 0xfd bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.RequestContext>.AsyncQueueReader.Set(System.Runtime.InputQueue<System.ServiceModel.Channels.RequestContext>.Item item) + 0x44 bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.RequestContext>.EnqueueAndDispatch(System.Runtime.InputQueue<System.ServiceModel.Channels.RequestContext>.Item item, bool canDispatchOnThisThread) + 0x1aa bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.RequestContext>.EnqueueAndDispatch(System.ServiceModel.Channels.RequestContext item, System.Action dequeuedCallback, bool canDispatchOnThisThread) + 0x5e bytes    
    System.ServiceModel.dll!System.ServiceModel.Channels.SingletonChannelAcceptor<System.ServiceModel.Channels.IReplyChannel,System.ServiceModel.Channels.ReplyChannel,System.ServiceModel.Channels.RequestContext>.Enqueue(System.ServiceModel.Channels.RequestContext item, System.Action dequeuedCallback, bool canDispatchOnThisThread) + 0x6b bytes    
    System.ServiceModel.dll!System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(System.ServiceModel.Channels.HttpRequestContext context, System.Action callback) + 0x1b4 bytes    
    System.ServiceModel.Activation.dll!System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(System.ServiceModel.Activation.HostedHttpRequestAsyncResult result) + 0xd6 bytes    
    System.ServiceModel.Activation.dll!System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest() + 0x232 bytes    
    System.ServiceModel.Activation.dll!System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest() + 0x27 bytes    
    System.ServiceModel.Activation.dll!System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(object state) + 0x49 bytes    
    System.ServiceModel.Activation.dll!System.ServiceModel.AspNetPartialTrustHelpers.PartialTrustInvoke(System.Threading.ContextCallback callback, object state) + 0x35 bytes    
    System.ServiceModel.Activation.dll!System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequestWithFlow(object state) + 0x7a bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped) + 0x78 bytes    
    System.Runtime.DurableInstancing.dll!System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(uint error, uint bytesRead, System.Threading.NativeOverlapped* nativeOverlapped) + 0x39 bytes    
    mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x74 bytes    
    [Native to Managed Transition]    
    [Appdomain Transition]    
    [Native to Managed Transition]    
4

1 回答 1

1

这里有几个问题:

  1. 我们在 WCF DS 中有一个错误。当我们在 .NET Framework 中时,很难考虑修复这个问题,因为可能会发生重大更改。现在我们已经脱离了框架并且我们有一个公开的问题,我们应该解决这个问题。
  2. 我们不枚举 IQueryable,这意味着 QueryProvider 永远不会真正执行查询。只要 getter 只是返回一个 IQueryable 并且不做任何工作,我们应该不会看到任何超过名义性能的影响。

综上所述,这是我们可以而且应该解决的问题,这样我们就不会让人们对此感到疑惑。如果我们有足够的带宽,我们将尝试在 5.0.2 或 5.1.0 中修复。

于 2012-08-01T17:33:07.800 回答