Summary

Class:Xrm.Oss.FluentQuery.FluentQuery`1
Assembly:Xrm.Oss.FluentQuery
File(s):D:\Entwicklung\Xrm-Fluent-Query\src\lib\Xrm.Oss.FluentQuery\FluentQuery.cs
Covered lines:143
Uncovered lines:7
Coverable lines:150
Total lines:1030
Line coverage:95.3%
Branch coverage:90%

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.ctor(...)10100100
IncludeColumns(...)10100100
IncludeAllColumns()10100100
RecordCount(...)10100100
DatabaseLock(...)10100100
PagingCookie(...)1000
GenerateQueryCacheKey(...)20100100
ReturnFromCache(...)34100100
SetCacheResult(...)22100100
Retrieve()54100100
RetrieveAll()93290.3281.82
UniqueRecords(...)10100100
Link(...)10100100
Where(...)10100100
AddCondition(...)10100100
AddFilter(...)10100100
Order(...)10100100
PagingInfo(...)10100100
TotalRecordCount(...)10100100
UseCache(...)10100100

File(s)

D:\Entwicklung\Xrm-Fluent-Query\src\lib\Xrm.Oss.FluentQuery\FluentQuery.cs

#LineLine coverage
 1/**
 2MIT License
 3
 4Copyright (c) 2018 Florian Krönert
 5
 6Permission is hereby granted, free of charge, to any person obtaining a copy
 7of this software and associated documentation files (the "Software"), to deal
 8in the Software without restriction, including without limitation the rights
 9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10copies of the Software, and to permit persons to whom the Software is
 11furnished to do so, subject to the following conditions:
 12
 13The above copyright notice and this permission notice shall be included in all
 14copies or substantial portions of the Software.
 15
 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22SOFTWARE.
 23*/
 24
 25using Microsoft.Xrm.Sdk;
 26using Microsoft.Xrm.Sdk.Query;
 27using System;
 28using System.Collections.Generic;
 29using System.Linq;
 30using System.Runtime.Caching;
 31using System.Runtime.Serialization.Json;
 32using System.IO;
 33using System.Text;
 34
 35namespace Xrm.Oss.FluentQuery
 36{
 37    public static class IOrganizationServiceFluentQuery
 38    {
 39        /**
 40         * <summary>
 41         * Creates a new fluent query for your early bound entity of type T, while automatically using the entity name o
 42         * If developing late bound, use the non-generic Query function.
 43         * </summary>
 44         * <returns>Fluent Query object. Use Retrieve or RetrieveAll for getting the results.</returns>
 45         */
 46        public static IFluentQuery<T> Query<T>(this IOrganizationService service) where T : Entity, new()
 47        {
 48            return new FluentQuery<T>(new T().LogicalName, service);
 49        }
 50
 51        /**
 52         * <summary>
 53         * Creates a new fluent query for your early bound entity of type T, while automatically using the entity name o
 54         * If developing late bound, use the non-generic Query function.
 55         * </summary>
 56         * <param name="entityName">The logical name of the entity you want to query.</param>
 57         * <returns>Fluent Query object. Use Retrieve or RetrieveAll for getting the results.</returns>
 58         */
 59        [Obsolete("Entity name is no longer needed for early bound queries, use the parameterless overload. For late bou
 60        public static IFluentQuery<T> Query<T>(this IOrganizationService service, string entityName) where T : Entity, n
 61        {
 62            return new FluentQuery<T>(entityName, service);
 63        }
 64
 65        /**
 66         * <summary>
 67         * Creates a new fluent query in late binding style. Results will be of type Entity.
 68         * If developing early bound, use the generic Query function.
 69         * </summary>
 70         * <param name="entityName">The logical name of the entity you want to query.</param>
 71         * <returns>Fluent Query object. Use Retrieve or RetrieveAll for getting the results.</returns>
 72         */
 73        public static IFluentQuery<Entity> Query(this IOrganizationService service, string entityName)
 74        {
 75            return new FluentQuery<Entity>(entityName, service);
 76        }
 77    }
 78
 79    public interface IFluentQuery<T> where T : Entity
 80    {
 81        /**
 82         * <summary>
 83         * Adds the given columns to your query. Multiple calls will just add to the existing columns.
 84         * </summary>
 85         * <param name="columns">Params array of your columns.</param>
 86         */
 87        IFluentQuery<T> IncludeColumns(params string[] columns);
 88
 89        /**
 90         * <summary>
 91         * Adds all columns to the query. This is disadvised, specify the columns you need if possible.
 92         * </summary>
 93         */
 94        IFluentQuery<T> IncludeAllColumns();
 95
 96        /**
 97         * <summary>
 98         * Returns the Query Expression that represents the current fluent query.
 99         * </summary>
 100         */
 101        QueryExpression Expression { get; }
 102
 103        /**
 104         * <summary>
 105         * Retrieves the first page for your query.
 106         * </summary>
 107         * <returns>Records retrieved from your query.</returns>
 108         */
 109        List<T> Retrieve();
 110
 111        /**
 112         * <summary>
 113         * Retrieves all pages for your query.
 114         * </summary>
 115         * <returns>Records retrieved from your query.</returns>
 116         */
 117        List<T> RetrieveAll();
 118
 119        /**
 120         * <summary>
 121         * Use this for setting further options in your query.
 122         * </summary>
 123         */
 124        IFluentQuerySetting<T> With { get; }
 125
 126        /**
 127         * <summary>
 128         * Adds a link to a connected entity.
 129         * </summary>
 130         * <param name="definition">Action for setting the link properties. Use a lambda for readability.</param>
 131         */
 132        IFluentQuery<T> Link(Action<IFluentLinkEntity> definition);
 133
 134        /**
 135         * <summary>
 136         * Adds filter conditions to your query.
 137         * </summary>
 138         * <remarks>Multiple calls to this method currently override the existing filter.</remarks>
 139         * <param name="definition">Action for setting the filter properties. Use a lambda for readability.</param>
 140         */
 141        IFluentQuery<T> Where(Action<IFluentFilterExpression> definition);
 142
 143        /**
 144         * <summary>
 145         * Instructs to use the supplied cache.
 146         * Cache keys will automatically be generated by the used query expression.
 147         * Retrieving and setting of the cache items is done automatically when executing retrieve.
 148         * </summary>
 149         * <param name="cache">The memory cache to use for getting and setting results</param>
 150         * <param name="absoluteExpiration">Expiration date for cached results</param>
 151         */
 152        IFluentQuery<T> UseCache(MemoryCache cache, DateTimeOffset absoluteExpiration);
 153
 154        /**
 155         * <summary>
 156         * Adds another condition to the top level filter expression.
 157         * </summary>
 158         * <param name="definition">The condition expression to add.</param>
 159         */
 160        void AddCondition(Action<IFluentConditionExpression> definition);
 161
 162        /**
 163         * <summary>
 164         * Adds a child filter to your top level filter.
 165         * </summary>
 166         * <param name="definition">Action for setting the filter properties. Use a lambda for readability.</param>
 167         */
 168        void AddFilter(Action<IFluentFilterExpression> definition);
 169
 170        /**
 171         * <summary>
 172         * Adds an order expression to your query.
 173         * </summary>
 174         * <param name="definition">Action for setting the order properties. Use a lambda for readability.</param>
 175         */
 176        IFluentQuery<T> Order(Action<IFluentOrderExpression> definition);
 177    }
 178
 179    public interface IFluentQuerySetting<T> where T : Entity
 180    {
 181        /**
 182         * <summary>
 183         * Set this for defining how many records you want to retrieve. The first top X records will be retrieved.
 184         * </summary>
 185         * <param name="topCount">Top X count of records to retrieve</param>
 186         */
 187        IFluentQuery<T> RecordCount(int? topCount);
 188
 189        /**
 190         * <summary>
 191         * Defines whether the record should be locked for retrieval. Not locking is a recommended best practice, but mi
 192         * </summary>
 193         * <remarks>Default of true is recommended as best practice. Dirty reads might occur if data is written to this 
 194         * <param name="useLock">True for locking the database record for retrieval, false otherwise.</param>
 195         */
 196        IFluentQuery<T> DatabaseLock(bool useLock = true);
 197
 198        /**
 199         * <summary>
 200         * Specifies whether duplicate records in your query will be filtered out or not.
 201         * </summary>
 202         * <param name="unique">True for only returning unique records, false otherwise</param>
 203         */
 204        IFluentQuery<T> UniqueRecords(bool unique = true);
 205
 206        /**
 207         * <summary>
 208         * Adds paging info to your query, such as page size, page number or paging cookie.
 209         * </summary>
 210         * <remarks>Use retrieve all for automatic retrieval of all records using paging.</remarks>
 211         * <param name="definition">Action for setting the paging info properties. Use a lambda for readability.</param>
 212         */
 213        IFluentQuery<T> PagingInfo(Action<IFluentPagingInfo> definition);
 214
 215        /**
 216         * <summary>
 217         * Determines whether to use a paging cookie when retrieving all records with paging. Speeds up retrieval, but m
 218         * More on this topic: https://truenorthit.co.uk/2014/07/19/dynamics-crm-paging-cookies-some-gotchas/
 219         * </summary>
 220         * <remarks>Paging Cookies are not used by default</remarks>
 221         * <param name="useCookie">True for using cookie, false otherwise</param>
 222         */
 223        IFluentQuery<T> PagingCookie(bool useCookie = true);
 224
 225        /**
 226         * <summary>
 227         * Specifies whether the total record count of your query results should be retrieved.
 228         * </summary>
 229         * <param name="returnTotalRecordCount">True for returning total record count, false otherwise.</param>
 230         */
 231        IFluentQuery<T> TotalRecordCount(bool returnTotalRecordCount = true);
 232    }
 233
 234    public class FluentQuery<T> : IFluentQuery<T>, IFluentQuerySetting<T> where T : Entity
 235    {
 236        private QueryExpression _query;
 237        private bool _usePagingCookie;
 238
 239        private IOrganizationService _service;
 240        private MemoryCache _cache;
 241        private DateTimeOffset _absoluteExpiration;
 242
 37243        public FluentQuery(string entityName, IOrganizationService service)
 37244        {
 37245            _query = new QueryExpression
 37246            {
 37247                EntityName = entityName,
 37248                NoLock = true
 37249            };
 250
 37251            _service = service;
 37252            _usePagingCookie = false;
 37253        }
 254
 255        public IFluentQuery<T> IncludeColumns(params string[] columns)
 11256        {
 11257            _query.ColumnSet.AllColumns = false;
 11258            _query.ColumnSet.AddColumns(columns);
 259
 11260            return this;
 11261        }
 262
 263        public IFluentQuery<T> IncludeAllColumns()
 7264        {
 7265            _query.ColumnSet.AllColumns = true;
 266
 7267            return this;
 7268        }
 269
 270        public IFluentQuerySetting<T> With
 271        {
 272            get
 7273            {
 7274                return this;
 7275            }
 276        }
 277
 278        public IFluentQuery<T> RecordCount(int? topCount)
 1279        {
 1280            _query.TopCount = topCount;
 281
 1282            return this;
 1283        }
 284
 285        public IFluentQuery<T> DatabaseLock(bool useLock = true)
 1286        {
 1287            _query.NoLock = !useLock;
 288
 1289            return this;
 1290        }
 291
 292        public IFluentQuery<T> PagingCookie(bool useCookie = true)
 0293        {
 0294            _usePagingCookie = useCookie;
 295
 0296            return this;
 0297        }
 298
 299        private string GenerateQueryCacheKey(QueryExpression query)
 8300        {
 8301            using (var memoryStream = new MemoryStream())
 8302            {
 8303                var serializer = new DataContractJsonSerializer(typeof(QueryExpression));
 8304                serializer.WriteObject(memoryStream, query);
 305
 8306                var serialized = Encoding.UTF8.GetString(memoryStream.ToArray());
 8307                return serialized;
 308            }
 8309        }
 310
 311        public List<T> ReturnFromCache(string key)
 12312        {
 12313            if (_cache != null)
 8314            {
 8315                if (_cache.Contains(key))
 2316                {
 2317                    return _cache.Get(key) as List<T>;
 318                }
 6319            }
 320
 10321            return null;
 12322        }
 323
 324        public void SetCacheResult(string key, List<T> result)
 10325        {
 10326            if (_cache != null)
 6327            {
 6328                _cache.Set(key, result, _absoluteExpiration);
 6329            }
 10330        }
 331
 332        public List<T> Retrieve()
 7333        {
 7334            var cacheKey = _cache != null ? GenerateQueryCacheKey(_query) : null;
 7335            var cacheResult = ReturnFromCache(cacheKey);
 336
 7337            if (cacheResult != null)
 1338            {
 1339                return cacheResult;
 340            }
 341
 12342            var result = _service.RetrieveMultiple(_query).Entities.Select(e => e.ToEntity<T>())
 6343                .ToList();
 344
 6345            SetCacheResult(cacheKey, result);
 346
 6347            return result;
 7348        }
 349
 350        public List<T> RetrieveAll()
 5351        {
 5352            var cacheKey = _cache != null ? GenerateQueryCacheKey(_query) : null;
 5353            var cacheResult = ReturnFromCache(cacheKey);
 354
 5355            if (cacheResult != null)
 1356            {
 1357                return cacheResult;
 358            }
 359
 4360            var records = new List<T>();
 361
 362            // Default to 1 as first page number, otherwise the first page is retrieved twice (once while supplying 0 as
 4363            var previousPageNumber = _query.PageInfo.PageNumber > 0 ? _query.PageInfo.PageNumber : 1;
 4364            var previousPagingCookie = _query.PageInfo.PagingCookie;
 365
 4366            var moreRecords = false;
 4367            var pageNumber = previousPageNumber;
 4368            string pagingCookie = previousPagingCookie;
 369
 370            do
 4371            {
 4372                _query.PageInfo.PageNumber = pageNumber;
 373
 4374                if (_usePagingCookie)
 0375                {
 0376                    _query.PageInfo.PagingCookie = pagingCookie;
 0377                }
 378
 4379                var response = _service.RetrieveMultiple(_query);
 9380                var result = response.Entities.Select(e => e.ToEntity<T>())
 4381                    .ToList();
 382
 4383                records.AddRange(result);
 384
 4385                moreRecords = response.MoreRecords;
 4386                pagingCookie = response.PagingCookie;
 387
 4388                pageNumber++;
 4389            }
 4390            while (moreRecords);
 391
 4392            _query.PageInfo.PageNumber = previousPageNumber;
 4393            _query.PageInfo.PagingCookie = previousPagingCookie;
 394
 4395            SetCacheResult(cacheKey, records);
 396
 4397            return records;
 5398        }
 399
 400        public IFluentQuery<T> UniqueRecords(bool unique = true)
 1401        {
 1402            _query.Distinct = true;
 403
 1404            return this;
 1405        }
 406
 407        public IFluentQuery<T> Link(Action<IFluentLinkEntity> definition)
 6408        {
 6409            var link = new FluentLinkEntity();
 410
 6411            definition(link);
 412
 6413            _query.LinkEntities.Add(link.GetLinkEntity());
 414
 6415            return this;
 6416        }
 417
 418        public IFluentQuery<T> Where(Action<IFluentFilterExpression> definition)
 6419        {
 6420            var filter = new FluentFilterExpression();
 421
 6422            definition(filter);
 423
 6424            _query.Criteria = filter.GetFilter();
 425
 6426            return this;
 6427        }
 428
 429        public void AddCondition(Action<IFluentConditionExpression> definition)
 1430        {
 1431            var condition = new FluentConditionExpression();
 432
 1433            definition(condition);
 434
 1435            _query.Criteria.AddCondition(condition.GetCondition());
 1436        }
 437
 438        public void AddFilter(Action<IFluentFilterExpression> definition)
 2439        {
 2440            var filter = new FluentFilterExpression();
 441
 2442            definition(filter);
 443
 2444            _query.Criteria.AddFilter(filter.GetFilter());
 2445        }
 446
 447        public IFluentQuery<T> Order(Action<IFluentOrderExpression> definition)
 5448        {
 5449            var order = new FluentOrderExpression();
 450
 5451            definition(order);
 452
 5453            _query.Orders.Add(order.GetOrder());
 454
 5455            return this;
 5456        }
 457
 458        public IFluentQuery<T> PagingInfo(Action<IFluentPagingInfo> definition)
 3459        {
 3460            var PagingInfo = new FluentPagingInfo();
 461
 3462            definition(PagingInfo);
 463
 3464            _query.PageInfo = PagingInfo.GetPagingInfo();
 465
 3466            return this;
 3467        }
 468
 469        public IFluentQuery<T> TotalRecordCount(bool returnTotalRecordCount = true)
 1470        {
 1471            _query.PageInfo.ReturnTotalRecordCount = true;
 472
 1473            return this;
 1474        }
 475
 476        public IFluentQuery<T> UseCache(MemoryCache cache, DateTimeOffset absoluteExpiration)
 8477        {
 8478            _cache = cache;
 8479            _absoluteExpiration = absoluteExpiration;
 480
 8481            return this;
 8482        }
 483
 484        public QueryExpression Expression
 485        {
 486            get
 25487            {
 25488                return _query;
 25489            }
 490        }
 491    }
 492
 493    public interface IFluentLinkEntity
 494    {
 495        /**
 496         * <summary>
 497         * Logical name of entity that the link is created from.
 498         * </summary>
 499         * <param name="entityName">Entity Logical Name</param>
 500         */
 501        IFluentLinkEntity FromEntity(string entityName);
 502
 503        /**
 504         * <summary>
 505         * Logical name of attribute that the link is created from.
 506         * </summary>
 507         * <param name="attributeName">Attribute Logical Name</param>
 508         */
 509        IFluentLinkEntity FromAttribute(string attributeName);
 510
 511        /**
 512         * <summary>
 513         * Logical name of entity that the link is created to.
 514         * </summary>
 515         * <param name="entityName">Entity Logical Name</param>
 516         */
 517        IFluentLinkEntity ToEntity(string entityName);
 518
 519        /**
 520         * <summary>
 521         * Logical name of attribute that the link is created to.
 522         * </summary>
 523         * <param name="attributeName">Attribute Logical Name</param>
 524         */
 525        IFluentLinkEntity ToAttribute(string attributeName);
 526
 527        /**
 528         * <summary>
 529         * Adds the given columns to the link entity. Multiple calls will just add to the existing columns.
 530         * </summary>
 531         * <param name="columns">Params array of your columns.</param>
 532         */
 533        IFluentLinkEntity IncludeColumns(params string[] columns);
 534
 535        /**
 536        * <summary>
 537        * Use this for setting further options of your link.
 538        * </summary>
 539        */
 540        IFluentLinkEntitySetting With { get; }
 541
 542        /**
 543         * <summary>
 544         * Adds a nested link to this link.
 545         * </summary>
 546         * <param name="definition">Action for setting the link properties. Use a lambda for readability.</param>
 547         */
 548        IFluentLinkEntity Link(Action<IFluentLinkEntity> definition);
 549
 550        /**
 551         * <summary>
 552         * Adds filter conditions to your link.
 553         * </summary>
 554         * <remarks>Multiple calls to this method currently override the existing filter.</remarks>
 555         * <param name="definition">Action for setting the filter properties. Use a lambda for readability.</param>
 556         */
 557        IFluentLinkEntity Where(Action<IFluentFilterExpression> definition);
 558    }
 559
 560    public interface IFluentLinkEntitySetting
 561    {
 562        /**
 563         * <summary>
 564         * Sets an alias for the results of this link entity.
 565         * </summary>
 566         * <param name="name">Alias to set in results.</param>
 567         */
 568        IFluentLinkEntity Alias(string name);
 569
 570        /**
 571         * <summary>Join type of this link.</summary>
 572         * <param name="joinOperator">Join type to use.</param>
 573         */
 574        IFluentLinkEntity LinkType(JoinOperator joinOperator);
 575    }
 576
 577    public class FluentLinkEntity : IFluentLinkEntity, IFluentLinkEntitySetting
 578    {
 579        private LinkEntity _linkEntity;
 580
 581        public FluentLinkEntity()
 582        {
 583            _linkEntity = new LinkEntity
 584            {
 585                Columns = new ColumnSet()
 586            };
 587        }
 588
 589        public IFluentLinkEntitySetting With
 590        {
 591            get
 592            {
 593                return this;
 594            }
 595        }
 596
 597        public IFluentLinkEntity Alias(string name)
 598        {
 599            _linkEntity.EntityAlias = name;
 600
 601            return this;
 602        }
 603
 604        public IFluentLinkEntity FromAttribute(string attributeName)
 605        {
 606            _linkEntity.LinkFromAttributeName = attributeName;
 607
 608            return this;
 609        }
 610
 611        public IFluentLinkEntity FromEntity(string entityName)
 612        {
 613            _linkEntity.LinkFromEntityName = entityName;
 614
 615            return this;
 616        }
 617
 618        public IFluentLinkEntity IncludeColumns(params string[] columns)
 619        {
 620            _linkEntity.Columns.AddColumns(columns);
 621
 622            return this;
 623        }
 624
 625        public IFluentLinkEntity Where(Action<IFluentFilterExpression> definition)
 626        {
 627            var filter = new FluentFilterExpression();
 628
 629            definition(filter);
 630
 631            _linkEntity.LinkCriteria = filter.GetFilter();
 632
 633            return this;
 634        }
 635
 636        public IFluentLinkEntity Link(Action<IFluentLinkEntity> definition)
 637        {
 638            var link = new FluentLinkEntity();
 639
 640            definition(link);
 641
 642            _linkEntity.LinkEntities.Add(link.GetLinkEntity());
 643
 644            return this;
 645        }
 646
 647        public IFluentLinkEntity LinkType(JoinOperator joinOperator)
 648        {
 649            _linkEntity.JoinOperator = joinOperator;
 650
 651            return this;
 652        }
 653
 654        public IFluentLinkEntity ToAttribute(string attributeName)
 655        {
 656            _linkEntity.LinkToAttributeName = attributeName;
 657
 658            return this;
 659        }
 660
 661        public IFluentLinkEntity ToEntity(string entityName)
 662        {
 663            _linkEntity.LinkToEntityName = entityName;
 664
 665            return this;
 666        }
 667
 668        internal LinkEntity GetLinkEntity()
 669        {
 670            return _linkEntity;
 671        }
 672    }
 673
 674    public interface IFluentFilterExpression
 675    {
 676        /**
 677        * <summary>
 678        * Use this for setting further options of your filter.
 679        * </summary>
 680        */
 681        IFluentFilterExpressionSetting With { get; }
 682
 683        /**
 684        * <summary>
 685        * Use this for adding a condition on an attribute to your filter.
 686        * </summary>
 687        * <remarks>Multiple calls to Attribute will add to the existing ones.</remarks>
 688        * <param name="definition">Action for setting the attribute properties. Use a lambda for readability.</param>
 689        */
 690        IFluentFilterExpression Attribute(Action<IFluentConditionExpression> definition);
 691
 692        /**
 693         * <summary>
 694         * Adds nested filter conditions to your filter.
 695         * </summary>
 696         * <remarks>Multiple calls to this method add to the existing filter conditions.</remarks>
 697         * <param name="definition">Action for setting the filter properties. Use a lambda for readability.</param>
 698         */
 699        IFluentFilterExpression Where(Action<IFluentFilterExpression> definition);
 700    }
 701
 702    public interface IFluentFilterExpressionSetting
 703    {
 704        /**
 705         * <summary>
 706         * Sets the logical operator for chaining multiple conditions in this filter.
 707         * </summary>
 708         */
 709        IFluentFilterExpression Operator(LogicalOperator filterOperator);
 710    }
 711
 712    public class FluentFilterExpression : IFluentFilterExpression, IFluentFilterExpressionSetting
 713    {
 714        private FilterExpression _filter;
 715
 716        public FluentFilterExpression()
 717        {
 718            _filter = new FilterExpression();
 719        }
 720
 721        public IFluentFilterExpressionSetting With
 722        {
 723            get
 724            {
 725                return this;
 726            }
 727        }
 728
 729        public IFluentFilterExpression Operator(LogicalOperator filterOperator)
 730        {
 731            _filter.FilterOperator = filterOperator;
 732
 733            return this;
 734        }
 735
 736        public IFluentFilterExpression Where(Action<IFluentFilterExpression> definition)
 737        {
 738            var filter = new FluentFilterExpression();
 739
 740            definition(filter);
 741
 742            _filter.Filters.Add(filter.GetFilter());
 743
 744            return this;
 745        }
 746
 747        public IFluentFilterExpression Attribute(Action<IFluentConditionExpression> definition)
 748        {
 749            var condition = new FluentConditionExpression();
 750
 751            definition(condition);
 752
 753            _filter.Conditions.Add(condition.GetCondition());
 754
 755            return this;
 756        }
 757
 758        internal FilterExpression GetFilter()
 759        {
 760            return _filter;
 761        }
 762    }
 763
 764    public interface IFluentConditionExpression
 765    {
 766        /**
 767         * <summary>
 768         * Set the entity name that your condition attribute targets, if it is not the main entity.
 769         * </summary>
 770         * <param name="entityName">Entity Logical Name</param>
 771         */
 772        IFluentConditionExpression Of(string entityName);
 773
 774        /**
 775         * <summary>
 776         * Set the logical name of the attribute that your condition targets.
 777         * </summary>
 778         * <param name="attributeName">Attribute logical name</param>
 779         */
 780        IFluentConditionExpression Named(string attributeName);
 781
 782        /**
 783         * <summary>
 784         * Set the condition operator for your condition.
 785         * </summary>
 786         * <param name="conditionOperator">Condition Operator Enum</param>
 787         */
 788        IFluentConditionExpression Is(ConditionOperator conditionOperator);
 789
 790        /**
 791         * <summary>
 792         * Sets the value for the condition.
 793         * </summary>
 794         * <param name="value">Single object, use object array overload if necessary.</param>
 795         */
 796        IFluentConditionExpression Value<T>(T value);
 797
 798        /**
 799         * <summary>
 800         * Sets the values for the condition.
 801         * </summary>
 802         * <param name="value">Object enumeration, use object overload if necessary.</param>
 803         */
 804        IFluentConditionExpression Values<T>(IEnumerable<T> values);
 805
 806        /**
 807         * <summary>
 808         * Alias for Value, provides better readability on Equal conditions.
 809         * Sets the value for the condition.
 810         * </summary>
 811         * <param name="value">Single object, use object array overload if necessary.</param>
 812         */
 813        IFluentConditionExpression To<T>(T value);
 814
 815        /**
 816        * <summary>
 817        * Alias for Value, provides better readability on Equal conditions.
 818        * Sets the values for the condition.
 819        * </summary>
 820        * <param name="value">Object enumeration, use object overload if necessary.</param>
 821        */
 822        IFluentConditionExpression ToMany<T>(IEnumerable<T> values);
 823    }
 824
 825    public class FluentConditionExpression : IFluentConditionExpression
 826    {
 827        private ConditionExpression _condition;
 828
 829        public FluentConditionExpression()
 830        {
 831            _condition = new ConditionExpression();
 832        }
 833
 834        public IFluentConditionExpression Is(ConditionOperator conditionOperator)
 835        {
 836            _condition.Operator = conditionOperator;
 837
 838            return this;
 839        }
 840
 841        public IFluentConditionExpression Named(string attributeName)
 842        {
 843            _condition.AttributeName = attributeName;
 844
 845            return this;
 846        }
 847
 848        public IFluentConditionExpression Of(string entityName)
 849        {
 850            _condition.EntityName = entityName;
 851
 852            return this;
 853        }
 854
 855        public IFluentConditionExpression To<T>(T value)
 856        {
 857            return Value(value);
 858        }
 859
 860        public IFluentConditionExpression ToMany<T>(IEnumerable<T> values)
 861        {
 862            return Values(values);
 863        }
 864
 865        public IFluentConditionExpression Value<T>(T value)
 866        {
 867            _condition.Values.Add(value);
 868
 869            return this;
 870        }
 871
 872        public IFluentConditionExpression Values<T>(IEnumerable<T> values)
 873        {
 874            foreach (var value in values)
 875            {
 876                _condition.Values.Add(value);
 877            }
 878
 879            return this;
 880        }
 881
 882        internal ConditionExpression GetCondition ()
 883        {
 884            return _condition;
 885        }
 886    }
 887
 888    public interface IFluentOrderExpression
 889    {
 890        /**
 891         * <summary>
 892         * Set the attribute name to order by.
 893         * </summary>
 894         * <param name="attributeName">Attribute logical name</param>
 895         */
 896        IFluentOrderExpression By(string attributeName);
 897
 898        /**
 899         * <summary>
 900         * Sets the sort order to be ascending.
 901         * </summary>
 902         */
 903        IFluentOrderExpression Ascending();
 904
 905        /**
 906         * <summary>
 907         * Sets the sort order to be descending.
 908         * </summary>
 909         */
 910        IFluentOrderExpression Descending();
 911    }
 912
 913    public class FluentOrderExpression : IFluentOrderExpression
 914    {
 915        private OrderExpression _order;
 916
 917        public FluentOrderExpression ()
 918        {
 919            _order = new OrderExpression();
 920        }
 921
 922        public IFluentOrderExpression By(string attributeName)
 923        {
 924            _order.AttributeName = attributeName;
 925
 926            return this;
 927        }
 928
 929        public IFluentOrderExpression Ascending()
 930        {
 931            _order.OrderType = OrderType.Ascending;
 932
 933            return this;
 934        }
 935
 936        public IFluentOrderExpression Descending()
 937        {
 938            _order.OrderType = OrderType.Descending;
 939
 940            return this;
 941        }
 942
 943        internal OrderExpression GetOrder()
 944        {
 945            return _order;
 946        }
 947    }
 948
 949    public interface IFluentPagingInfo
 950    {
 951        /**
 952         * <summary>
 953         * Set the page number to retrieve. Is set to 1 by default.
 954         * </summary>
 955         * <param name="number">Number of the page, starts at 1.</param>
 956         */
 957        IFluentPagingInfo PageNumber(int number);
 958
 959        /**
 960         * <summary>
 961         * Set the paging cookie for retrieving records from pages after the first.
 962         * </summary>
 963         * <remarks>Use retrieve all for automatic retrieval of all records using paging.</remarks>
 964         * <param name="pagingCookie">Paging cookie retrieved during last query response.</param>
 965         */
 966        IFluentPagingInfo PagingCookie(string pagingCookie);
 967
 968        /**
 969         * <summary>
 970         * Set the size of each page.
 971         * </summary>
 972         * <param name="number">Number of records to return per page.</param>
 973         */
 974        IFluentPagingInfo PageSize(int number);
 975
 976        /**
 977         * <summary>
 978         * Specifies whether the total record count of your query results should be retrieved.
 979         * </summary>
 980         * <param name="returnTotal">True for returning total record count, false otherwise.</param>
 981         */
 982        IFluentPagingInfo ReturnTotalRecordCount(bool returnTotal = true);
 983    }
 984
 985    public class FluentPagingInfo : IFluentPagingInfo
 986    {
 987        private PagingInfo _pagingInfo;
 988
 989        public FluentPagingInfo()
 990        {
 991            _pagingInfo = new PagingInfo
 992            {
 993                PageNumber = 1
 994            };
 995        }
 996
 997        public IFluentPagingInfo PageNumber(int number)
 998        {
 999            _pagingInfo.PageNumber = number;
 1000
 1001            return this;
 1002        }
 1003
 1004        public IFluentPagingInfo PageSize(int number)
 1005        {
 1006            _pagingInfo.Count = number;
 1007
 1008            return this;
 1009        }
 1010
 1011        public IFluentPagingInfo PagingCookie(string pagingCookie)
 1012        {
 1013            _pagingInfo.PagingCookie = pagingCookie;
 1014
 1015            return this;
 1016        }
 1017
 1018        public IFluentPagingInfo ReturnTotalRecordCount(bool returnTotal = true)
 1019        {
 1020            _pagingInfo.ReturnTotalRecordCount = returnTotal;
 1021
 1022            return this;
 1023        }
 1024
 1025        public PagingInfo GetPagingInfo()
 1026        {
 1027            return _pagingInfo;
 1028        }
 1029    }
 1030}