Last active
February 27, 2016 17:37
-
-
Save dfch/f00938e2cc3057e5957a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[AppclusiveAuthorize] | |
public async Task<IHttpActionResult> GetNodes(ODataQueryOptions<Node> queryOptions) | |
{ | |
Contract.Requires(null != queryOptions, "|400|"); | |
queryOptions.Validate(_validationSettings); | |
db.DisableGeneralTenantFilterForUberAdmin(); | |
var pageableFilterResult = db.Nodes.PageableEntityFilter(queryOptions, (entity, context) => | |
{ | |
if(context.AccessManager.HasPermission(entity, Permissions.NodesCanRead)) | |
{ | |
return true; | |
} | |
return false; | |
}); | |
this.SetNextLink(pageableFilterResult.EntitySet, pageableFilterResult.SkipCount); | |
return Ok<IEnumerable<Node>>(pageableFilterResult.EntitySet); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
GET http://localhost:53422/api/Core/Nodes()?$filter=Name%20ne%20'Root%20Node'&$orderby=Modified,%20Created%20desc,%20Id%20asc&$skip=5&$top=35 HTTP/1.1 | |
DataServiceVersion: 1.0;NetFx | |
MaxDataServiceVersion: 3.0;NetFx | |
Accept: application/json;odata=minimalmetadata | |
Accept-Charset: UTF-8 | |
User-Agent: Microsoft ADO.NET Data Services | |
Authorization: Basic UXVlc3Rpb246d2hvLXJlYWxseS1wb3N0cy1CQVNFNjQtZW5jb2RlZC1jcmVkZW50aWFscy10by1wdWJsaWMtd2ViLXNpdGVz | |
Host: localhost:53422 | |
HTTP/1.1 200 OK | |
Cache-Control: no-cache | |
Pragma: no-cache | |
Content-Type: application/json; odata=minimalmetadata; charset=utf-8 | |
Expires: -1 | |
Server: Microsoft-IIS/8.0 | |
Set-Cookie: biz.dfch.CS.Appclusive.Core.Security.AuthenticationFilters.MultiAuthenticationFilter=D4-05-FC-92-41-10-BB-31-6F-7C-18-B9-1F-0C-91-62-B8-E0-4F-2C-0C-F6-BE-A8-F2-53-19-76-06-A3-B4-5A | |
DataServiceVersion: 3.0 | |
X-AspNet-Version: 4.0.30319 | |
Persistent-Auth: true | |
X-Powered-By: ASP.NET | |
Date: Sat, 27 Feb 2016 15:57:30 GMT | |
Content-Length: 5819 | |
{ | |
"odata.metadata":"http://localhost:53422/api/Core/$metadata#Nodes","value":[ | |
{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"18","Tid":"22222222-2222-2222-2222-222222222222","Name":"still","Description":"still is a basque for mainland purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:51.2250094+01:00","Modified":"2016-02-19T13:05:51.2250094+01:00","RowVersion":"AAAAAAAAjWM=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"20","Tid":"22222222-2222-2222-2222-222222222222","Name":"mariner","Description":"mariner is a covert for derma purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:51.8220436+01:00","Modified":"2016-02-19T13:05:51.8220436+01:00","RowVersion":"AAAAAAAAjW8=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"20","Tid":"22222222-2222-2222-2222-222222222222","Name":"mariner","Description":"mariner is a covert for derma purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:51.8220436+01:00","Modified":"2016-02-19T13:05:51.8220436+01:00","RowVersion":"AAAAAAAAjW8=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"22","Tid":"22222222-2222-2222-2222-222222222222","Name":"vitellin","Description":"vitellin is a squeeze for varices purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:52.3860758+01:00","Modified":"2016-02-19T13:05:52.3860758+01:00","RowVersion":"AAAAAAAAjXs=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"24","Tid":"22222222-2222-2222-2222-222222222222","Name":"missal","Description":"missal is a resale for vendace purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:53.0281126+01:00","Modified":"2016-02-19T13:05:53.0281126+01:00","RowVersion":"AAAAAAAAjYc=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"26","Tid":"22222222-2222-2222-2222-222222222222","Name":"oxidase","Description":"oxidase is a ultra for mannish purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:53.6611488+01:00","Modified":"2016-02-19T13:05:53.6611488+01:00","RowVersion":"AAAAAAAAjZM=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"28","Tid":"22222222-2222-2222-2222-222222222222","Name":"terrapin","Description":"terrapin is a fraenum for caroche purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:54.259183+01:00","Modified":"2016-02-19T13:05:54.259183+01:00","RowVersion":"AAAAAAAAjZ8=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"30","Tid":"22222222-2222-2222-2222-222222222222","Name":"stagnate","Description":"stagnate is a potheen for pitman purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:55.0032255+01:00","Modified":"2016-02-19T13:05:55.0032255+01:00","RowVersion":"AAAAAAAAjas=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"32","Tid":"22222222-2222-2222-2222-222222222222","Name":"fraenum","Description":"fraenum is a hindmost for vendace purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:55.5642576+01:00","Modified":"2016-02-19T13:05:55.5642576+01:00","RowVersion":"AAAAAAAAjbc=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"34","Tid":"22222222-2222-2222-2222-222222222222","Name":"matins","Description":"matins is a baronet for overtake purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:56.1272898+01:00","Modified":"2016-02-19T13:05:56.1272898+01:00","RowVersion":"AAAAAAAAjcM=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"36","Tid":"22222222-2222-2222-2222-222222222222","Name":"unwashed","Description":"unwashed is a petrosal for aside purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:56.9203352+01:00","Modified":"2016-02-19T13:05:56.9203352+01:00","RowVersion":"AAAAAAAAjc8=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"38","Tid":"22222222-2222-2222-2222-222222222222","Name":"revolute","Description":"revolute is a mannish for leal purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:57.721381+01:00","Modified":"2016-02-19T13:05:57.721381+01:00","RowVersion":"AAAAAAAAjds=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"40","Tid":"22222222-2222-2222-2222-222222222222","Name":"toneme","Description":"toneme is a papaya for lollygag purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:05:59.0604576+01:00","Modified":"2016-02-19T13:05:59.0604576+01:00","RowVersion":"AAAAAAAAjec=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"42","Tid":"22222222-2222-2222-2222-222222222222","Name":"leaky","Description":"leaky is a pitman for leal purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:06:00.3735327+01:00","Modified":"2016-02-19T13:06:00.3735327+01:00","RowVersion":"AAAAAAAAjfM=" | |
},{ | |
"EntityId":null,"Parameters":"{}","EntityKindId":"23","ParentId":"1","Id":"44","Tid":"22222222-2222-2222-2222-222222222222","Name":"lurdan","Description":"lurdan is a resale for oodles purposes","CreatedById":"3","ModifiedById":"3","Created":"2016-02-19T13:06:01.2015801+01:00","Modified":"2016-02-19T13:06:01.2015801+01:00","RowVersion":"AAAAAAAAjf8=" | |
} | |
],"odata.nextLink":"http://localhost:53422/api/Core/Nodes()?$filter=Name%20ne%20'Root%20Node'&$orderby=Modified,%20Created%20desc,%20Id%20asc&$top=35&$skip=41" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2016-02-27 18:36:15,206 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - p__linq__0: Root Node | |
2016-02-27 18:36:15,207 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - p__linq__1: 0 | |
2016-02-27 18:36:15,207 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - p__linq__2: 16 | |
2016-02-27 18:36:15,213 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - Intercepted on: ReaderExecuted :- IsAsync: False, Command Text: SELECT | |
[Project1].[Id] AS [Id], | |
[Project1].[EntityId] AS [EntityId], | |
[Project1].[Parameters] AS [Parameters], | |
[Project1].[EntityKindId] AS [EntityKindId], | |
[Project1].[ParentId] AS [ParentId], | |
[Project1].[Tid] AS [Tid], | |
[Project1].[Name] AS [Name], | |
[Project1].[Description] AS [Description], | |
[Project1].[CreatedById] AS [CreatedById], | |
[Project1].[ModifiedById] AS [ModifiedById], | |
[Project1].[Created] AS [Created], | |
[Project1].[Modified] AS [Modified], | |
[Project1].[RowVersion] AS [RowVersion] | |
FROM ( SELECT | |
[Limit1].[Id] AS [Id], | |
[Limit1].[EntityId] AS [EntityId], | |
[Limit1].[Parameters] AS [Parameters], | |
[Limit1].[EntityKindId] AS [EntityKindId], | |
[Limit1].[ParentId] AS [ParentId], | |
[Limit1].[Tid] AS [Tid], | |
[Limit1].[Name] AS [Name], | |
[Limit1].[Description] AS [Description], | |
[Limit1].[CreatedById] AS [CreatedById], | |
[Limit1].[ModifiedById] AS [ModifiedById], | |
[Limit1].[Created] AS [Created], | |
[Limit1].[Modified] AS [Modified], | |
[Limit1].[RowVersion] AS [RowVersion] | |
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[EntityId] AS [EntityId], [Extent1].[Parameters] AS [Parameters], [Extent1].[EntityKindId] AS [EntityKindId], [Extent1].[ParentId] AS [ParentId], [Extent1].[Tid] AS [Tid], [Extent1].[Name] AS [Name], [Extent1].[Description] AS [Description], [Extent1].[CreatedById] AS [CreatedById], [Extent1].[ModifiedById] AS [ModifiedById], [Extent1].[Created] AS [Created], [Extent1].[Modified] AS [Modified], [Extent1].[RowVersion] AS [RowVersion] | |
FROM [core].[Node] AS [Extent1] | |
ORDER BY [Extent1].[Id] ASC | |
OFFSET 15 ROWS FETCH NEXT 16 ROWS ONLY | |
) AS [Limit1] | |
WHERE (([Limit1].[Tid] = @DynamicFilterParam_1) OR (@DynamicFilterParam_2 IS NOT NULL)) AND (([Limit1].[Tid] = @DynamicFilterParam_3) OR (@DynamicFilterParam_4 IS NOT NULL)) AND ( NOT (([Limit1].[Name] = @p__linq__0) AND (0 = (CASE WHEN (@p__linq__0 IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END)))) | |
) AS [Project1] | |
ORDER BY [Project1].[Modified] ASC, [Project1].[Created] DESC, [Project1].[Id] ASC | |
OFFSET @p__linq__1 ROWS FETCH NEXT @p__linq__2 ROWS ONLY | |
2016-02-27 18:36:15,222 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - p__linq__0: Root Node | |
2016-02-27 18:36:15,223 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - p__linq__1: 0 | |
2016-02-27 18:36:15,223 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - p__linq__2: 16 | |
2016-02-27 18:36:15,230 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - Intercepted on: ReaderExecuted :- IsAsync: False, Command Text: SELECT | |
[Project1].[Id] AS [Id], | |
[Project1].[EntityId] AS [EntityId], | |
[Project1].[Parameters] AS [Parameters], | |
[Project1].[EntityKindId] AS [EntityKindId], | |
[Project1].[ParentId] AS [ParentId], | |
[Project1].[Tid] AS [Tid], | |
[Project1].[Name] AS [Name], | |
[Project1].[Description] AS [Description], | |
[Project1].[CreatedById] AS [CreatedById], | |
[Project1].[ModifiedById] AS [ModifiedById], | |
[Project1].[Created] AS [Created], | |
[Project1].[Modified] AS [Modified], | |
[Project1].[RowVersion] AS [RowVersion] | |
FROM ( SELECT | |
[Limit1].[Id] AS [Id], | |
[Limit1].[EntityId] AS [EntityId], | |
[Limit1].[Parameters] AS [Parameters], | |
[Limit1].[EntityKindId] AS [EntityKindId], | |
[Limit1].[ParentId] AS [ParentId], | |
[Limit1].[Tid] AS [Tid], | |
[Limit1].[Name] AS [Name], | |
[Limit1].[Description] AS [Description], | |
[Limit1].[CreatedById] AS [CreatedById], | |
[Limit1].[ModifiedById] AS [ModifiedById], | |
[Limit1].[Created] AS [Created], | |
[Limit1].[Modified] AS [Modified], | |
[Limit1].[RowVersion] AS [RowVersion] | |
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[EntityId] AS [EntityId], [Extent1].[Parameters] AS [Parameters], [Extent1].[EntityKindId] AS [EntityKindId], [Extent1].[ParentId] AS [ParentId], [Extent1].[Tid] AS [Tid], [Extent1].[Name] AS [Name], [Extent1].[Description] AS [Description], [Extent1].[CreatedById] AS [CreatedById], [Extent1].[ModifiedById] AS [ModifiedById], [Extent1].[Created] AS [Created], [Extent1].[Modified] AS [Modified], [Extent1].[RowVersion] AS [RowVersion] | |
FROM [core].[Node] AS [Extent1] | |
ORDER BY [Extent1].[Id] ASC | |
OFFSET 31 ROWS FETCH NEXT 16 ROWS ONLY | |
) AS [Limit1] | |
WHERE (([Limit1].[Tid] = @DynamicFilterParam_1) OR (@DynamicFilterParam_2 IS NOT NULL)) AND (([Limit1].[Tid] = @DynamicFilterParam_3) OR (@DynamicFilterParam_4 IS NOT NULL)) AND ( NOT (([Limit1].[Name] = @p__linq__0) AND (0 = (CASE WHEN (@p__linq__0 IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END)))) | |
) AS [Project1] | |
ORDER BY [Project1].[Modified] ASC, [Project1].[Created] DESC, [Project1].[Id] ASC | |
OFFSET @p__linq__1 ROWS FETCH NEXT @p__linq__2 ROWS ONLY | |
2016-02-27 18:36:15,233 [36] INFO biz.dfch.CS.Utilities.Logging.LogBase [(null)] <(null)> - 00000000-0000-0000-5300-0080020000fb-END [200] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static PageableEntityFilterResult<TEntity> PageableEntityFilter<TEntity> | |
( | |
this IQueryable<TEntity> queryable | |
, | |
ODataQueryOptions<TEntity> queryOptions | |
, | |
Func<TEntity, PageableEntityFilterContext, bool> filter | |
) | |
where TEntity : BaseEntity | |
{ | |
Contract.Requires(null != queryable); | |
Contract.Requires(null != queryOptions); | |
Contract.Requires(null != filter); | |
Contract.Ensures(null != Contract.Result<PageableEntityFilterResult<TEntity>>()); | |
// accessManager will be passed down to the filter | |
var accessManager = new AccessManager(); | |
var context = new PageableEntityFilterContext(); | |
context.AccessManager = accessManager; | |
// get skip count and top count if specified | |
// round down top count to max pageSize+1 | |
var originalSkipCount = null != queryOptions.Skip ? queryOptions.Skip.Value : 0; | |
var skipCount = 0; | |
var topCount = null != queryOptions.Top ? | |
Math.Min(queryOptions.Top.Value, Constants.ODATA_ENABLEQUERY_PAGESIZE + 1) : | |
(Constants.ODATA_ENABLEQUERY_PAGESIZE + 1); | |
// entitySet holds all entities that have passed the filter expression | |
var entitySet = new List<TEntity>(); | |
var orderedEntitySetCount = 0; | |
do | |
{ | |
// get the result set with filter and order applied | |
// for queries with top < pageSize we end up with a bad query | |
// as we potentially have re-query the database too often. | |
// Therefore we always query pageSize+1 records from the table and | |
// break the loop if we either have top records -or- pageSize+1 records | |
IQueryable<TEntity> orderedResultSet = queryOptions.ApplyOrderedTo<TEntity>(queryable, skipCount, Constants.ODATA_ENABLEQUERY_PAGESIZE + 1); | |
orderedEntitySetCount = 0; | |
foreach (TEntity entity in orderedResultSet) | |
{ | |
// count returned entities (regardless of filter expression) | |
orderedEntitySetCount++; | |
var isEntityToBeAdded = filter(entity, context); | |
if(isEntityToBeAdded) | |
{ | |
// apply the skip count | |
if(originalSkipCount > 0) | |
{ | |
--originalSkipCount; | |
} | |
else | |
{ | |
// add entity to cache | |
CacheManager.Default.Add<TEntity>(entity); | |
// add entity to resultSet | |
entitySet.Add(entity); | |
} | |
} | |
// abort if page size (+1) is reached, +1 is necessary to determine if we need a next link | |
if (entitySet.Count >= Constants.ODATA_ENABLEQUERY_PAGESIZE + 1 || entitySet.Count >= topCount) | |
{ | |
break; | |
} | |
} | |
// adjust skip count for next iteration (regardless of actually returned entities) | |
skipCount += orderedEntitySetCount; | |
} | |
// continue while | |
// we have returned entries AND | |
// we have not yet reached the specified topCount | |
while (0 < orderedEntitySetCount && topCount > entitySet.Count); | |
// decrease skip count as we are now off by one | |
--skipCount; | |
// return list a IQueryable | |
return new PageableEntityFilterResult<TEntity>() | |
{ | |
EntitySet = entitySet | |
, | |
SkipCount = skipCount | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PageableEntityFilterContext | |
{ | |
public AccessManager AccessManager; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PageableEntityFilterResult<TEntity> | |
where TEntity : BaseEntity | |
{ | |
public IList<TEntity> EntitySet; | |
public int SkipCount; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ODataQueryOptionsExtensionsImpl | |
{ | |
public IQueryable ApplyOrderedTo<TSource>(ODataQueryOptions<TSource> oDataQueryOptions, IQueryable<TSource> entitySet, ODataQuerySettings querySettings) | |
where TSource : BaseEntity | |
{ | |
var topCount = querySettings.PageSize.HasValue ? querySettings.PageSize.Value : 0; | |
var result = ApplyOrderedTo<TSource>(oDataQueryOptions, entitySet, 0, topCount); | |
return result; | |
} | |
public IQueryable<TSource> ApplyOrderedTo<TSource>(ODataQueryOptions<TSource> oDataQueryOptions, IQueryable<TSource> entitySet, int skipCount, int topCount) | |
where TSource : BaseEntity | |
{ | |
Contract.Requires(null != oDataQueryOptions); | |
Contract.Requires(null != entitySet); | |
Contract.Ensures(null != Contract.Result<IQueryable>()); | |
var originalSkipCount = null != oDataQueryOptions.Skip ? oDataQueryOptions.Skip.Value : 0; | |
var originalTopCount = null != oDataQueryOptions.Top ? oDataQueryOptions.Top.Value : 0; | |
SetSkipOption(oDataQueryOptions, 0); | |
SetTopOption(oDataQueryOptions, topCount); | |
var orderByPropertyExtractor = new ODataOrderByPropertyExtractor(); | |
IQueryable resultSet; | |
if (null != oDataQueryOptions.OrderBy && null != oDataQueryOptions.OrderBy.OrderByClause) | |
{ | |
IOrderedQueryable<TSource> orderedQueryable; | |
// get first orderby clause | |
var orderByClauseIndex = 0; | |
var orderByPropertyName = orderByPropertyExtractor.GetPropertyName(oDataQueryOptions.OrderBy.RawValue, orderByClauseIndex); | |
var orderByPropertyType = orderByPropertyExtractor.GetPropertyType(oDataQueryOptions.OrderBy, orderByClauseIndex); | |
#region entitySet.OrderBy(orderByPropertyName) | |
if (oDataQueryOptions.OrderBy.OrderByClause.Direction == Microsoft.Data.OData.Query.OrderByDirection.Ascending) | |
{ | |
if (typeof(DateTimeOffset) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderBy<TSource, DateTimeOffset>(orderByPropertyName); | |
} | |
else if (typeof(long) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderBy<TSource, long>(orderByPropertyName); | |
} | |
else if (typeof(string) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderBy<TSource, string>(orderByPropertyName); | |
} | |
else if (typeof(int) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderBy<TSource, int>(orderByPropertyName); | |
} | |
else if (typeof(Guid) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderBy<TSource, Guid>(orderByPropertyName); | |
} | |
else if (typeof(DateTime) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderBy<TSource, DateTime>(orderByPropertyName); | |
} | |
else | |
{ | |
Contract.Assert(null != orderByPropertyType, "Unsupported orderBy type"); | |
orderedQueryable = null; | |
} | |
} | |
else | |
{ | |
if (typeof(DateTimeOffset) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderByDescending<TSource, DateTimeOffset>(orderByPropertyName); | |
} | |
else if (typeof(long) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderByDescending<TSource, long>(orderByPropertyName); | |
} | |
else if (typeof(string) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderByDescending<TSource, string>(orderByPropertyName); | |
} | |
else if (typeof(int) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderByDescending<TSource, int>(orderByPropertyName); | |
} | |
else if (typeof(Guid) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderByDescending<TSource, Guid>(orderByPropertyName); | |
} | |
else if (typeof(DateTime) == orderByPropertyType) | |
{ | |
orderedQueryable = entitySet.OrderByDescending<TSource, DateTime>(orderByPropertyName); | |
} | |
else | |
{ | |
Contract.Assert(null != orderByPropertyType, "Unsupported orderBy type"); | |
orderedQueryable = null; | |
} | |
} | |
#endregion | |
// get subsequent orderby clauses | |
var thenBy = oDataQueryOptions.OrderBy.OrderByClause.ThenBy; | |
while (null != thenBy) | |
{ | |
orderByClauseIndex++; | |
var thenByPropertyName = orderByPropertyExtractor.GetPropertyName(oDataQueryOptions.OrderBy.RawValue, orderByClauseIndex); | |
var thenByPropertyType = orderByPropertyExtractor.GetPropertyType(oDataQueryOptions.OrderBy, orderByClauseIndex); | |
#region orderedQueryable.OrderBy(thenByPropertyName); | |
if (thenBy.Direction == Microsoft.Data.OData.Query.OrderByDirection.Ascending) | |
{ | |
if (typeof(DateTimeOffset) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderBy<TSource, DateTimeOffset>(thenByPropertyName); | |
} | |
else if (typeof(long) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderBy<TSource, long>(thenByPropertyName); | |
} | |
else if (typeof(string) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderBy<TSource, string>(thenByPropertyName); | |
} | |
else if (typeof(int) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderBy<TSource, int>(thenByPropertyName); | |
} | |
else if (typeof(Guid) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderBy<TSource, Guid>(thenByPropertyName); | |
} | |
else if (typeof(DateTime) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderBy<TSource, DateTime>(thenByPropertyName); | |
} | |
else | |
{ | |
Contract.Assert(null != thenByPropertyName, "Unsupported orderBy type"); | |
orderedQueryable = null; | |
} | |
} | |
else | |
{ | |
if (typeof(DateTimeOffset) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderByDescending<TSource, DateTimeOffset>(thenByPropertyName); | |
} | |
else if (typeof(long) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderByDescending<TSource, long>(thenByPropertyName); | |
} | |
else if (typeof(string) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderByDescending<TSource, string>(thenByPropertyName); | |
} | |
else if (typeof(int) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderByDescending<TSource, int>(thenByPropertyName); | |
} | |
else if (typeof(Guid) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderByDescending<TSource, Guid>(thenByPropertyName); | |
} | |
else if (typeof(DateTime) == thenByPropertyType) | |
{ | |
orderedQueryable = orderedQueryable.OrderByDescending<TSource, DateTime>(thenByPropertyName); | |
} | |
else | |
{ | |
Contract.Assert(null != thenByPropertyName, "Unsupported orderBy type"); | |
orderedQueryable = null; | |
} | |
} | |
#endregion | |
// get next orderby clause | |
thenBy = thenBy.ThenBy; | |
} | |
// execute ordered query | |
resultSet = oDataQueryOptions.ApplyTo | |
( | |
orderedQueryable | |
.Skip(skipCount) | |
.Take(topCount) | |
); | |
} | |
else | |
{ | |
// append default orderby and execute query | |
resultSet = oDataQueryOptions.ApplyTo | |
(entitySet | |
.OrderBy(e => e.Id) | |
.Skip(skipCount) | |
.Take(topCount) | |
); | |
} | |
SetSkipOption(oDataQueryOptions, originalSkipCount); | |
SetTopOption(oDataQueryOptions, originalTopCount); | |
return resultSet.Cast<TSource>(); | |
} | |
public bool SetSkipOption<TSource>(ODataQueryOptions<TSource> oDataQueryOptions, int value) | |
where TSource : BaseEntity | |
{ | |
Contract.Requires(null != oDataQueryOptions); | |
Contract.Requires(0 <= value); | |
if (null == oDataQueryOptions.Skip) | |
{ | |
return false; | |
} | |
if(oDataQueryOptions.Skip.Value == value) | |
{ | |
return true; | |
} | |
var fieldInfoRawValue = GetBackingFieldInfo(oDataQueryOptions.Skip, "RawValue"); | |
fieldInfoRawValue.SetValue(oDataQueryOptions.Skip, value.ToString()); | |
var runtimeFieldInfoValue = oDataQueryOptions.Skip.GetType().GetRuntimeFields().FirstOrDefault(e => e.Name == "_value"); | |
runtimeFieldInfoValue.SetValue(oDataQueryOptions.Skip, value); | |
Contract.Assert(oDataQueryOptions.Skip.Value == value); | |
Contract.Assert(oDataQueryOptions.Skip.RawValue == value.ToString()); | |
return true; | |
} | |
public bool SetTopOption<TSource>(ODataQueryOptions<TSource> oDataQueryOptions, int value) | |
where TSource : BaseEntity | |
{ | |
Contract.Requires(null != oDataQueryOptions); | |
Contract.Requires(0 <= value); | |
if (null == oDataQueryOptions.Top) | |
{ | |
return false; | |
} | |
if (oDataQueryOptions.Top.Value == value) | |
{ | |
return true; | |
} | |
var fieldInfoRawValue = GetBackingFieldInfo(oDataQueryOptions.Top, "RawValue"); | |
fieldInfoRawValue.SetValue(oDataQueryOptions.Top, value.ToString()); | |
var runtimeFieldInfoValue = oDataQueryOptions.Top.GetType().GetRuntimeFields().FirstOrDefault(e => e.Name == "_value"); | |
runtimeFieldInfoValue.SetValue(oDataQueryOptions.Top, value); | |
Contract.Assert(oDataQueryOptions.Top.Value == value); | |
Contract.Assert(oDataQueryOptions.Top.RawValue == value.ToString()); | |
return true; | |
} | |
public int GetTakeCount<TSource>(ODataQueryOptions<TSource> queryOptions, int pageSize = Constants.ODATA_ENABLEQUERY_PAGESIZE) | |
where TSource : BaseEntity | |
{ | |
Contract.Requires(null != queryOptions); | |
var count = null != queryOptions.Top ? Math.Min(queryOptions.Top.Value, pageSize) : pageSize; | |
return ++count; | |
} | |
private string GetBackingFieldName(string propertyName) | |
{ | |
Contract.Ensures(!string.IsNullOrWhiteSpace(Contract.Result<string>())); | |
var result = string.Format("<{0}>k__BackingField", propertyName); | |
return result; | |
} | |
private FieldInfo GetBackingFieldInfo(object obj, string propertyName) | |
{ | |
Contract.Ensures(null != Contract.Result<FieldInfo>()); | |
var fieldName = GetBackingFieldName(propertyName); | |
var type = obj.GetType(); | |
var backingFieldInfo = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); | |
return backingFieldInfo; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ODataControllerExtensionsImpl | |
{ | |
public Uri SetNextLink<TEntity>(ODataController controller, IList<TEntity> entitySet, int pageSize, int skipCount) | |
where TEntity : BaseEntity | |
{ | |
Contract.Requires(null != controller); | |
Contract.Requires(null != entitySet); | |
Contract.Requires(0 < pageSize); | |
var oDataProperties = controller.Request.ODataProperties(); | |
if(0 >= entitySet.Count) | |
{ | |
return oDataProperties.NextLink; | |
} | |
if (pageSize >= entitySet.Count) | |
{ | |
return oDataProperties.NextLink; | |
} | |
Contract.Assert(0 <= skipCount); | |
// DFTODO - currently we have an issue when the resultSet.Count is 1 AND the pagesoze is 1 | |
Contract.Assert(1 <= entitySet.Count); | |
entitySet.RemoveAt(entitySet.Count - 1); | |
var absoluteUri = controller.Request.RequestUri.AbsoluteUri; | |
var nextLinkBase = Regex.Replace(absoluteUri, "\\$skip=\\d+", "").TrimEnd('&'); | |
var nextLinkUri = string.Format("{0}&$skip={1}", nextLinkBase, skipCount).Replace("&&", "&"); | |
oDataProperties.NextLink = new Uri(nextLinkUri); | |
return oDataProperties.NextLink; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2014-2016 d-fens GmbH | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment