为什么WCF服务在持久化新实体之前执行query all



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

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

我使用了下面发布的以下代码,并在Provider.cs中设置了断点。当我用HttpRequesterFirefox插件调用它时,根据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; }
    }
}

Provider.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]    

这里有几个问题:

  1. 我们在WCF DS中有一个错误。当我们使用.NET Framework时,很难考虑修复这个错误,因为可能会破坏更改。既然我们已经脱离了框架,我们有一个公开的要求,我们应该解决这个问题
  2. 我们不枚举IQueryable,这意味着QueryProvider永远不会实际执行查询。只要getter只是返回了一个IQueryable,并且没有做任何工作,我们就不应该看到它对性能的影响

也就是说,这是我们可以也应该解决的问题,这样我们就不会让人们对此感到疑惑。如果我们有带宽的话,我们会在5.0.2或5.1.0中尝试解决。

最新更新