为什么WCF服务在持久化新实体之前执行所有查询?

时间:2012-08-01 08:09:03

标签: c# .net wcf-data-services odata

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

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

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

  1. IQueryable被称为
  2. CreateResource被称为
  3. 多次调用SetValue
  4. 称为SaveChanges
  5. 我有一个问题,为什么要调用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;
            }
        }
    }
    

    Employee.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 个答案:

答案 0 :(得分:1)

这里有几个问题:

  1. 我们在WCF DS中有一个错误。我们在.NET Framework中很难考虑解决这个问题,因为有可能会发生变化。现在我们已经不在框架中了,我们有公开要求,我们应该解决这个问题。
  2. 我们不枚举IQueryable,这意味着QueryProvider永远不会实际执行查询。只要吸气剂只是返回一个IQueryable并且没有做任何工作,我们就不应该看到由此产生的名义性能。
  3. 所有这些都说,这是我们可以而且应该解决的问题,这样我们就不会让人们对此感到疑惑。如果我们有足够的带宽,我们将尝试在5.0.2或5.1.0中修复。