1

我正在处理的项目在 OData 层之上具有实体框架。Odata 层将其服务器端分页设置为75. 我对这个主题的阅读让我相信这个分页值是全面使用的,而不是每张桌子的基础。我目前要从中提取所有数据的表当然超过 75 行。使用实体框架,我的代码就是这样:

public IQueryable<ProductColor> GetProductColors()
{
   return db.ProductColors;
}

db实体上下文在哪里。这将返回前 75 条记录。我读了一些东西,我可以在其中附加一个参数inlinecount集来allpages给我以下代码:

public IQueryable<ProductColor> GetProductColors()
{
   return db.ProductColors.AddQueryOption("inlinecount","allpages");
}

但是,这也返回 75 行!

无论 OData 服务器端分页内容如何,​​任何人都可以阐明如何真正获取所有记录吗?

重要:我无法删除分页或将其关闭!在其他需要关注性能的场景中,它非常有价值。

更新: 通过更多搜索,我找到了描述如何执行此任务的MSDN 。

我希望能够将它变成一个完整的通用方法,但是,这与我可以在不使用反射的情况下获得的通用方法一样接近:

public IQueryable<T> TakeAll<T>(QueryOperationResponse<T> qor)
    {
      var collection = new List<T>();
      DataServiceQueryContinuation<T> next = null;
      QueryOperationResponse<T> response = qor;
      do
      {
        if (next != null)
        {
          response = db.Execute<T>(next) as QueryOperationResponse<T>;
        }

        foreach (var elem in response)
        {
          collection.Add(elem);          
        }

      } while ((next = response.GetContinuation()) != null);

      return collection.AsQueryable();
    }

像这样称呼它:

public IQueryable<ProductColor> GetProductColors()
    {      
      QueryOperationResponse<ProductColor> response = db.ProductColors.Execute() as QueryOperationResponse<ProductColor>;
      var productColors = this.TakeAll<ProductColor>(response);
      return productColors.AsQueryable();
    }
4

2 回答 2

6

如果无法关闭寻呼,您将始终收到 75 行的呼叫。您可以通过以下方式获取所有行:

  1. 添加另一个IQueryable<ProductColor> AllProductColors并修改

    public static void InitializeService(DataServiceConfiguration config)
    {
        config.UseVerboseErrors = true;
        config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
        config.SetEntitySetPageSize("ProductColors", 75); - Note only paged queries are present
        config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
    }
    
  2. 例如,您应该根据需要尽可能多地调用 ProductColors

        var cat = new NetflixCatalog(new Uri("http://odata.netflix.com/v1/Catalog/"));
    
        var x = from t in cat.Titles
                where t.ReleaseYear == 2009
                select t;
        var response = (QueryOperationResponse<Title>)((DataServiceQuery<Title>)x).Execute();
    
        while (true)
        {
            foreach (Title title in response)
            {
                Console.WriteLine(title.Name);
            }
    
            var continuation = response.GetContinuation();
            if (continuation == null)
            {
                break;
            }
    
            response = cat.Execute(continuation);
        }
    

我使用带有以下代码的 Rx

public sealed class DataSequence<TEntry> : IObservable<TEntry>
{
    private readonly DataServiceContext context;
    private readonly Logger logger = LogManager.GetCurrentClassLogger();
    private readonly IQueryable<TEntry> query;

    public DataSequence(IQueryable<TEntry> query, DataServiceContext context)
    {
        this.query = query;
        this.context = context;
    }

    public IDisposable Subscribe(IObserver<TEntry> observer)
    {
        QueryOperationResponse<TEntry> response;
        try
        {
            response = (QueryOperationResponse<TEntry>)((DataServiceQuery<TEntry>)query).Execute();
            if (response == null)
            {
                return Disposable.Empty;
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex);
            return Disposable.Empty;
        }
        var initialState = new State
                               {
                                   CanContinue = true,
                                   Response = response
                               };
        IObservable<TEntry> sequence = Observable.Generate(
            initialState,
            state => state.CanContinue,
            MoveToNextState,
            GetCurrentValue,
            Scheduler.ThreadPool).Merge();
        return new CompositeDisposable(initialState, sequence.Subscribe(observer));
    }

    private static IObservable<TEntry> GetCurrentValue(State state)
    {
        if (state.Response == null)
        {
            return Observable.Empty<TEntry>();
        }
        return state.Response.ToObservable();
    }

    private State MoveToNextState(State state)
    {
        DataServiceQueryContinuation<TEntry> continuation = state.Response.GetContinuation();
        if (continuation == null)
        {
            state.CanContinue = false;
            return state;
        }
        QueryOperationResponse<TEntry> response;
        try
        {
            response = context.Execute(continuation);
        }
        catch (Exception)
        {
            state.CanContinue = false;
            return state;
        }
        state.Response = response;
        return state;
    }

    private sealed class State : IDisposable
    {

        public bool CanContinue { get; set; }

        public QueryOperationResponse<TEntry> Response { get; set; }

        public void Dispose()
        {
            CanContinue = false;
        }
    }
}

因此,要通过 OData 获取任何数据,请创建一个序列,然后由 Rx 完成其余工作

 var sequence = new DataSequence<Product>(context.Products, context);
 sequence.OnErrorResumeNext(Observable.Empty<Product>())
            .ObserveOnDispatcher().SubscribeOn(Scheduler.NewThread).Subscribe(AddProduct, logger.Error);
于 2012-07-24T18:42:26.547 回答
2

页面大小由服务作者设置,并且可以为每个实体集设置(但服务可以选择将相同的页面大小应用于所有实体集)。无法从客户端避免它(这是设计使然,因为它是一项安全功能)。

inlinecount 选项要求服务器包含结果的总计数(只是数字),它不会禁用分页。

从客户端读取所有数据的唯一方法是发出将返回第一页的请求,它可能包含您请求读取下一页的下一个链接,依此类推,直到最后一个响应没有下一个关联。

如果您使用的是 WCF 数据服务客户端库,它支持延续(下一个链接),并且可以在此博客文章中找到一个简单的示例(例如):http: //blogs.msdn.com/b/phaniraj /archive/2010/04/25/server-driven-paging-with-wcf-data-services.aspx

于 2012-07-24T17:55:04.780 回答