MongoDB c# : Question about pagination - c#

Using a paged result of some query i need to get from what page is a point.The object is return the data positioned at the right page when you push the point out of the scope opening the paged result at this page.
If the paged result can be obtain like this sample , how i can get from an item from what page is comming ?
paging
.skip(PAGE_SIZE * (PAGE_NUMBER - 1)).limit(PAGE_SIZE)
public List<BsonItem> GetData(QueryComplete query, int take, int skip, SortByBuilder sort)
{
var cursor = Db.Data.FindAs<BsonItem>(query);
if (skip > 0)
cursor.SetSkip(skip);
if (take > 0)
cursor.SetLimit(take);
if (sort != null )
cursor.SetSortOrder(sort);
return cursor.ToList();
}
Thanks for your help.

Page number should( and possible sort order and direction) come from the client side. So client click on the some page and than..
Personally i using some kind of filter that contains all properties that you need.
Using that filter you just need to specify page that you need to display in grid and how much items you need per page.
var pageNumber = 1;// current page should come from the client
var filter = new BaseFilter(){CurrentPage = pageNumber, ItemsPerPage = 30};
var items = GetItemsByFilter(filter, Query.LTE("SomeDate",DateTime.Now)),
SortBy.Ascending("SortField"));
//For basic paging you only following three properties
var totalCount = filter.TotalCount; // here will be total items count
var pagesCount = filter.TotalPagesCount; // here will be total pages count
// pageNumber = current page
Also you can inferit from BasicFilter and add any properties that you need for quering, sorting.
Here filter code(hope it will be useful for you):
public List<Item> GetItemsByFilter(BaseFilter filter,
QueryComplete query, SortByBuilder sort)
{
var resultItems = new List<Item>();
var cursor = Db.Data.FindAs<BsonItem>(query);
cursor.SetSortOrder(sort);
if (filter.IsNeedPaging)
{
cursor.SetSkip(filter.Skip).SetLimit(filter.Take);
filter.TotalCount = cursor.Count();
}
resultItems.AddRange(cursor);
return resultItems;
}
public class BaseFilter
{
private int _itemsPerPage = 10;
private int _skip = 0;
private int _currentPage = 1;
public BaseFilter()
{
IsNeedPaging = true;
}
public int Skip
{
get
{
if (_skip == 0)
_skip = (CurrentPage - 1) * _itemsPerPage;
return _skip;
}
set
{
_skip = value;
}
}
public int Take
{
get
{
return _itemsPerPage;
}
set
{
_itemsPerPage = value;
}
}
public bool IsNeedPaging { get; set; }
public int TotalCount { get; set; }
public int CurrentPage
{
get
{
return _currentPage;
}
set
{
_currentPage = value;
}
}
public int ItemsPerPage
{
get
{
return _itemsPerPage;
}
set
{
_itemsPerPage = value;
}
}
public int TotalPagesCount
{
get
{
return TotalCount / ItemsPerPage +
((TotalCount % ItemsPerPage > 0) ? 1 : 0);
}
}
}

Related

Extension method with generic input and output object casting

This question is mostly not a problem help call, but an invitation for the comparison between developers on contemporary, more advanced and clean development methods.
Here I explain how I've resolved this problem and show you a working code example. Anyway, I have some doubts about my solution. I'm really curios if into nature exists a more elegant way to achieve the same code optimization.
I've started from the issue when I had two different controllers with mostly same response models except Items property type.
Precision: We need to have dedicated and not shared response models for each controller. In my opinion it helps in the future, when the only one controller's response have to be changed without creating side effects for the others.
I've started from the problem when I had two different controllers with mostly same response models except Items property type.
Here them are:
namespace Webapi.Models.File {
public class Response {
public FileItem [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
}
public class FileItem {
...
}
}
namespace Webapi.Models.User {
public class Response {
public UserItem [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
}
public class UserItem {
...
}
}
The first model is populated in this way:
using FileModel = Webapi.Models.File;
private FileModel.Response CreateItemsPage(List<FileModel.FileItem> items, int page) {
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if(totalChunks == 0) {
return null;
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
return new FileModel.Response() {
Items = (chunks.ToArray())[page-1].ToArray(),
Page = page,
TotalPages = totalChunks
};
}
And the second method is exactly the same except input (List<UserModel.UserItem>) and output (UserModel.Response) types:
using UserModel = Webapi.Models.User;
private UserModel.Response CreateItemsPage(List<UserModel.UserItem> items, int page) {
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if(totalChunks == 0) {
return null;
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
return new UserModel.Response() {
Items = (chunks.ToArray())[page-1].ToArray(),
Page = page,
TotalPages = totalChunks
};
}
Having two and then even more cloned methods inside my webapi's controllers isn't a good perspective and I have resolved this by creating two ObjectExtensions methods.
The first one just reassign properties from source to target object. Both logically must have same properties inside (name and type):
public static TTarget AssignProperties<TTarget, TSource>(this TTarget target, TSource source) {
foreach (var targetProp in target.GetType().GetProperties()) {
foreach (var sourceProp in source.GetType().GetProperties()) {
if (targetProp.Name == sourceProp.Name && targetProp.PropertyType == sourceProp.PropertyType) {
targetProp.SetValue(target, sourceProp.GetValue(source));
break;
}
}
}
return target;
}
The second receives target and source objects, creates internally an anonymous one, then reassigns properties from it by using the previous extension method AssignProperties to the target object (need this because cannot access generic objects properties directly):
public static TTarget CreateItemsPage<TTarget, TSource>(this TTarget target, List<TSource> items, int page = 1) {
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if(totalChunks == 0) {
return target;
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
var source = new {
Items = (chunks.ToArray())[page-1].ToArray(),
Page = page,
TotalPages = totalChunks
};
target = target.AssignProperties(source);
return target;
}
And here is the usage:
...
var items = _filesService.ListAllUserFiles(userId, requestData.SearchText);
if(pages.Count() == 0)
return BadRequest();
return Ok(new FileModel.Response().CreateItemsPage(items, requestData.Page));
...
Some code examples would be appreciated. Thank you!
By opening a discussion on Reddit I came to the following solution that allows me to keep models separate and remove the inefficient AssignProperties method.
Interface:
public interface IPaginationResponse<TItem> {
TItem[] Items { get; set; }
int Page { get; set; }
int TotalPages { get; set; }
}
Models examples:
public class Response: IPaginationResponse<Info> {
public Info [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
...
}
public class Response: IPaginationResponse<UserFile> {
public UserFile [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
...
}
public class Response: IPaginationResponse<UserItem> {
public UserItem [] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
...
}
Now finally I've removed AssignProperties from CreateItemsPage extension method. Thanks to where TTarget : IPaginationResponse<TSource> I can assign values directly to TTarget target
public static TTarget CreateItemsPage<TTarget, TSource>(this TTarget target, IEnumerable<TSource> items, int page = 1) where TTarget : IPaginationResponse<TSource> {
...
target.Items = (chunks.ToArray())[page-1].ToArray();
target.Page = page;
target.TotalPages = totalChunks;
return target;
}
Inside controller I invoke it in the same way
return Ok(new FileModel.Response().CreateItemsPage(pages, requestData.Page));
As both Response classes are different in Item type only, a single generic type could be used instead of two non-generic types.
public class ResponsePageTemplate<TItem>
{
public TItem[] Items { get; set; }
public int Page { get; set; }
public int TotalPages { get; set; }
}
Then extension method would be like:
public static ResponsePageTemplate<TSource> CreateItemsPage<TSource>(this IEnumerable<TSource> items, int page = 1)
{
int maxItemsPerPage = 50;
var chunks = items.Select((v, i) => new {Value = v, Index = i})
.GroupBy(x => x.Index / maxItemsPerPage).Select(grp => grp.Select(x => x.Value));
int totalChunks = chunks.Count();
if (totalChunks == 0)
{
return new ResponsePageTemplate<TSource>();
}
page = page > 1 ? page : 1;
page = totalChunks < page ? 1 : page;
var result = new ResponsePageTemplate<TSource>
{
Items = (chunks.ToArray())[page - 1].ToArray(),
Page = page,
TotalPages = totalChunks
};
return result;
}
And usage would be like:
return Ok(items.CreateItemsPage(requestData.Page));

How could I genericize a range expansion function?

I have the following class:
public class State {
public DateTime Created { get; set; }
public Double A { get; set; }
public Double B { get; set; }
}
Then I have two states:
State start = new State { Created = DateTime.UtcNow, A = 10.2, B = 20.5 }
State end = new State { Created = DateTime.UtcNow.AddDays(40), A = 1, B = 80 }
I would like to create a List between start and end where the values of A and B evolve in a linear way between their start and end values.
I was able to create a List as follows:
IList<DateTime> dates = new Range<DateTime>(start.Created, end.Created,).Expand(DateInterval.Day, 1)
Range and Expand are a few helpers I created ...
What is the best way to create the values evolution?
My idea would to do something like:
IList<State> states = new Range<State>( // here "tell" how each property would evolve ...)
UPDATE
Range Class
public class Range<T> where T : IComparable<T> {
private T _minimum;
private T _maximum;
public T Minimum { get { return _minimum; } set { value = _minimum; } }
public T Maximum { get { return _maximum; } set { value = _maximum; } }
public Range(T minimum, T maximum) {
_minimum = minimum;
_maximum = maximum;
} // Range
public Boolean Contains(T value) {
return (Minimum.CompareTo(value) <= 0) && (value.CompareTo(Maximum) <= 0);
} // Contains
public Boolean ContainsRange(Range<T> Range) {
return this.IsValid() && Range.IsValid() && this.Contains(Range.Minimum) && this.Contains(Range.Maximum);
} // ContainsRange
public Boolean IsInsideRange(Range<T> Range) {
return this.IsValid() && Range.IsValid() && Range.Contains(this.Minimum) && Range.Contains(this.Maximum);
} // IsInsideRange
public Boolean IsValid() {
return Minimum.CompareTo(Maximum) <= 0;
} // IsValid
public override String ToString() {
return String.Format("[{0} - {1}]", Minimum, Maximum);
} // ToString
} // Range
A few Range Extensions:
public static IEnumerable<Int32> Expand(this Range<Int32> range, Int32 step = 1) {
Int32 current = range.Minimum;
while (current <= range.Maximum) {
yield return current;
current += step;
}
} // Expand
public static IEnumerable<DateTime> Expand(this Range<DateTime> range, TimeSpan span) {
DateTime current = range.Minimum;
while (current <= range.Maximum) {
yield return current;
current = current.Add(span);
}
} // Expand
var magicNumber = 40;
var start = new State {Created = DateTime.UtcNow, A = 10.2, B = 20.5};
var end = new State {Created = DateTime.UtcNow.AddDays(magicNumber), A = 1, B = 80};
var stateList = new List<State>(magicNumber);
for (var i = 0; i < magicNumber; ++i)
{
var newA = start.A + (end.A - start.A) / magicNumber * (i + 1);
var newB = start.B + (end.B - start.B) / magicNumber * (i + 1);
stateList.Add(new State { Created = DateTime.UtcNow.AddDays(i + 1), A = newA, B = newB });
}
So you have moved to the stage of genericization where a "mutation step" injectable method that will apply your interpolation becomes logical. Because you don't constrain far enough to know how to interpolate steps, you may need a factory method to create your Func<T,T> instances.
For example, if there are two properties whose values shift in your T, you would be able to control their rate of change this way.
public class Point
{
int X {get;set;}
int Y {get;set;}
}
public static IEnumerable<T> Expand(this Range<T> range, Func<T,T> stepFunction)
where T: IComparable<T>
{
var current = range.Minimum;
while (range.Contains(current))
{
yield return current;
current = stepFunction(current);
}
}
// in method
var pointRange = new Range<Point>(
new Point{ X=0,Y=0}, new Point{ X=5, Y=20});
foreach (Point p in pointRange.Expand(p => new Point{ p.X + 1, p.Y + 4})

Compare properties of two objects fast c#

I have two objects of same type with different values:
public class Itemi
{
public Itemi()
{
}
public int Prop1Min { get; set; }
public int Prop1Max { get; set; }
public int Prop2Min { get; set; }
public int Prop2Max { get; set; }
public int Prop3Min { get; set; }
public int Prop3Max { get; set; }
...................................
public int Prop25Min { get; set; }
public int Prop25Max { get; set; }
}
Now I instantiate two objects of this type and add some values to their properties.
Itemi myItem1 = new Itemi();
myItem1.Prop1Min = 1;
myItem1.Prop1Max = 4;
myItem1.Prop2Min = 2;
myItem1.Prop2Max = 4;
myItem1.Prop3Min = -1;
myItem1.Prop3Max = 5;
.............................
myItem1.Prop25Min = 1;
myItem1.Prop25Max = 5;
Itemi myItem2 = new Itemi();
myItem2.Prop1Min = 1;
myItem2.Prop1Max = 5;
myItem2.Prop2Min = -10;
myItem2.Prop2Max = 3;
myItem2.Prop3Min = 0;
myItem2.Prop3Max = 2;
................................
myItem2.Prop25Min = 3;
myItem2.Prop25Max = 6;
What is the best and fastest way to do this comparison:
take each properties from myItem1 and check if values from Prop1-25 Min and Max are within the range values of myItem2 Prop1-25 Min and Max
Example:
myItem1.Prop1Min = 1
myItem1.Prop1Max = 4
myItem2.Prop1Min = 1
myItem2.Prop1Max = 5
this is True because mtItem1 Prop1 min and max are within the range of myItem2 min and max.
the condition should be AND in between all properties so in the end after we check all 25 properties if all of them are within the range of the second object we return true.
Is there a fast way to do this using Linq or other algorithm except the traditional if-else?
I would refactor the properties to be more along the lines of:
public class Item
{
public List<Range> Ranges { get; set; }
}
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
}
Then your comparison method could be:
if (myItem1.Ranges.Count != myItem2.Ranges.Count)
{
return false;
}
for (int i = 0; i < myItem1.Ranges.Count; i++)
{
if (myItem1.Ranges[i].Min < myItem2.Ranges[i].Min ||
myItem1.Ranges[i].Max > myItem2.Ranges[i].Max)
{
return false;
}
}
return true;
Otherwise you will have to use Reflection, which is anything but fast.
Linq is using standart statements like if...then, for each and other, there is no magic :)
If the final goal only to compare, without needing to say, which properties are not in the range, then you not need to check them all, on the first unequals you can end checking.
Because you have so much properties, you must think about saving it in Dictionary, or List, for example. Or to use dynamic properties (ITypedList), if it will use for binding.
You really should do something like Ginosaji proposed.
But if you want to go with your current data model, here is how I would solve it. Happy typing. :)
public static bool RangeIsContained(int outerMin, int outerMax, int innerMin, int innerMax)
{
return (outerMin <= innerMin && outerMax >= innerMax);
}
public bool IsContained(Itemi outer, Itemi inner)
{
return RangeIsContained(outer.Prop1Min, outer.Prop1Max, inner.Prop1Min, inner.Prop1Max)
&& RangeIsContained(outer.Prop2Min, outer.Prop2Max, inner.Prop2Min, inner.Prop2Max)
// ...
&& RangeIsContained(outer.Prop25Min, outer.Prop25Max, inner.Prop25Min, inner.Prop25Max);
}
With your data model this is basically the only way to go except for reflection (slow!). LINQ cannot help you because your data is not enumerable.
For the sake of completeness, here is a LINQ solution (but it's less performant and less readable than Ginosaji's solution!)
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
public static bool IsContained(Range super, Range sub)
{
return super.Min <= sub.Min
&& super.Max >= sub.Max;
}
}
public class Itemi
{
public Itemi()
{
properties = new Range[25];
for (int i = 0; i < properties.Length; i++)
{
properties[i] = new Range();
}
}
private Range[] properties;
public IEnumerable<Range> Properties { get { return properties; } }
public static bool IsContained(Itemi super, Itemi sub)
{
return super.properties
.Zip(sub.properties, (first, second) => Tuple.Create(first, second))
.All((entry) => Range.IsContained(entry.Item1, entry.Item2));
}
public Range Prop1
{
get { return properties[0]; }
set { properties[0] = value; }
}
public Range Prop2
{
get { return properties[1]; }
set { properties[1] = value; }
}
// ...
}

How to do pagination after every 10 comments in C#

I have a list of comments formed. The client wants a new page after every 10 comments, so comment 11 will be on page 2, and so on for as many pages as they get. The commets are formed on a .ashx page becuase of an issue I had with the regular .aspx.cs page. Simply put, how do I accomplish this?
Here is the code of the .ashx page:
public void ProcessRequest (HttpContext context)
{
// ****************************************
if (context.Request["postform"] == "1")
{
videomessage myVideoMessage = new videomessage();
myVideoMessage.video_id = context.Request["video_id"];
myVideoMessage.first_name_submitter = context.Request["first_name_submitter"];
myVideoMessage.last_initial_submitter = context.Request["last_initial_submitter"];
myVideoMessage.message = context.Request["message"];
myVideoMessage.status = "0";
myVideoMessage.Save();
}
// ****************************************
// ****************************************
StringBuilder myStringBuilder = new StringBuilder();
// PULL VIDEOMESSAGES FOR VIDEO_ID
videomessage[] myCommentsList = new videomessage().Listing("video_id", context.Request["video_id"], "entry_date" , "DESC");
// FORM COMMENTS IF MORE THAN ONE COMMENT EXISTS
foreach (videomessage tmpMessage in myCommentsList)
{
if (tmpMessage.status == "0" || tmpMessage.status == "1")
{
myStringBuilder.Append("<div class=\"comment_box\">");
myStringBuilder.Append("<p class=\"comment_date\">");
myStringBuilder.Append(Utility.FormatShortDate(tmpMessage.entry_date) + " " + tmpMessage.first_name_submitter + " " + tmpMessage.last_initial_submitter + "." + "</p>");
if (!String.IsNullOrEmpty(tmpMessage.message))
{
myStringBuilder.Append("<p>" + tmpMessage.message + "</p>");
myStringBuilder.Append("</div>");
}
}
}
string return_str = myStringBuilder.ToString();
// IF NO COMMENTS RETURN THIS
if( String.IsNullOrEmpty(return_str) ) return_str = "<p>No comments currently exist for this video.</p>";
// ****************************************
// RETURN STRING
context.Response.ContentType = "text/plain";
context.Response.Write(return_str);
}
I know there will need to be some math involved as well as assigning it to variables, but I am still new to .NET so any help would be appreciated.
Thanks in advance!
using Linq
const int pageSize = 10;
var paginatedComments = comments.Skip((page ?? 0) * pageSize).Take(pageSize).ToList();
(*) page is input from request
Maybe a little outdated, but have a look at the nerddinner tutorial (http://aspnetmvcbook.s3.amazonaws.com/aspnetmvc-nerdinner_v1.pdf), which describes a pagination principle for MVC using a custom PaginatedList class
public class PaginatedList<T> : List<T> {
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) {
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = source.Count();
TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);
this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}
public bool HasPreviousPage {
get {
return (PageIndex > 0);
}
}
public bool HasNextPage {
get {
return (PageIndex+1 < TotalPages);
}
}
}
Alternative solution would be using a jquery approach (see http://www.jquery4u.com/plugins/10-jquery-pagination-plugins), but I have no experience with either of the plugins suggested on this site.
Have a look at this
currentPage=1
NextPageFirstRecord=(currrentpage-1)*noRecordsPerPage
pageCount=totalrecords/noRecordsPerPage
Hope this helps

How to make paging start from 1 instead of 0

I used the paging example of the Nerddinner tutorial. But I also wanted to add page Numbers, somehting like that:
<<< 1 2 3 4 5 6 >>>
The code below works if i start my paging from 0, but not from 1. How can I fix this ?
Here is my code:
PaginatedList.cs
public class PaginatedList<T> : List<T> {
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) {
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = source.Count();
TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);
this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}
public bool HasPreviousPage {
get {
return (PageIndex > 0);
}
}
public bool HasNextPage {
get {
return (PageIndex+1 < TotalPages);
}
}
}
UserController.cs
public ActionResult List(int? page)
{
const int pageSize = 20;
IUserRepository userRepository = new UserRepository();
IQueryable<User> listUsers = userRepository.GetAll();
PaginatedList<User> paginatedUsers = new PaginatedList<User>(listUsers, page ?? 0, pageSize);
return View(paginatedUsers);
}
List.cshtml
#if (Model.HasPreviousPage)
{
#Html.RouteLink(" Previous ", "PaginatedUsers", new { page = (Model.PageIndex - 1) })
}
#for (int i = 1; i <= Model.TotalPages; i++)
{
#Html.RouteLink(#i.ToString(), "PaginatedUsers", new { page = (#i ) })
}
#if (Model.HasNextPage)
{
#Html.RouteLink(" Next ", "PaginatedUsers", new { page = (Model.PageIndex + 1) })
}
PaginatedList.cs
.Skip((PageIndex -1) * PageSize).Take(PageSize)
UserController.cs
public ActionResult List(int page = 1)
{
I have made this changes :
PaginatedList.cs
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) {
.
.
.
this.AddRange(source.Skip((PageIndex -1) * PageSize).Take(PageSize));
}
public bool HasPreviousPage {
get {
return (PageIndex > 1);
}
}
public bool HasNextPage {
get {
return (PageIndex < TotalPages);
}
}
UserController.cs
public ActionResult List(int? page)
{
const int pageSize = 20;
page = (page < 1) ? 1 : page ?? 0;
.
.
.
I would suggest to use a library instead of going to write your own pagination code. MvcPaging is one of those library.
See my answer here - what good libraries to use for paging on custom html (not table based)?

Categories