EF core 5 Paging Performance issue - entity-framework

I am using EF core 5. I am trying to retrieve records from the table having 1 Million records. I would like to know how I can improve my performance.
Note: I have already implemented the index in the DB. I did google but not find any solution. please advice
How to improve Skip and Take performance in EF core 5
public async Task<IEnumerable<User>> GetUsers(PaginationFilter filter)
{
**return await _context.User.AsNoTracking().Include(e => e.Roles)
.Skip((filter.PageNumber - 1) * filter.PageSize)
.Take(filter.PageSize)
.ToListAsync<User>();**
}
public class PaginationFilter
{
public int PageNumber { get; set; }
public int PageSize { get; set; }
public PaginationFilter()
{
this.PageNumber = 1;
this.PageSize = 10;
}
public PaginationFilter(int pageNumber, int pageSize)
{
this.PageNumber = pageNumber < 1 ? 1 : pageNumber;
this.PageSize = pageSize > 10 ? 10 : pageSize;
}
}
public static UsersPagedResponse<List<T>> CreateUsersPagedReponse<T>(List<T> pagedData, PaginationFilter validFilter, int totalRecords, IUriService uriService, string route)
{
var respose = new UsersPagedResponse<List<T>>(pagedData, validFilter.PageNumber, validFilter.PageSize);
var totalPages = ((double)totalRecords / (double)validFilter.PageSize);
int roundedTotalPages = Convert.ToInt32(Math.Ceiling(totalPages));
respose.MetaData = new MetaData()
{
Page = validFilter.PageNumber,
PageSize = validFilter.PageSize,
Links = new Links()
{
Self = uriService.GetPageUri(new PaginationFilter(validFilter.PageNumber, validFilter.PageSize), route),
First = uriService.GetPageUri(new PaginationFilter(1, validFilter.PageSize), route),
Previous = validFilter.PageNumber - 1 >= 1 && validFilter.PageNumber <= roundedTotalPages ? uriService.GetPageUri(new PaginationFilter(validFilter.PageNumber - 1, validFilter.PageSize), route) : null,
Next = validFilter.PageNumber >= 1 && validFilter.PageNumber < roundedTotalPages ? uriService.GetPageUri(new PaginationFilter(validFilter.PageNumber + 1, validFilter.PageSize), route) : null,
}
};
return respose;
}
}
public class UsersPagedResponse<T> : UsersResponse<T>
{
public MetaData MetaData { get; set; }
public UsersPagedResponse(T users, int pageNumber, int pageSize)
{
this.Users = users;
}
public UsersPagedResponse(T users)
{
this.Users = users;
}
}

Related

entity framework core 3 dynamic order by not working

public class Branch
{
[Sortable(OrderBy = "BranchId")]
public long BranchId { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Type { get; set; }
}
this is my Model class and I also create a custom attribute
public class SortableAttribute : Attribute
{
public string OrderBy { get; set; }
}
now i create a pagination with orderby descending but this code not working
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source,
GeneralPagingRequest pagingRequest, int indexFrom = 0,
CancellationToken cancellationToken = default(CancellationToken))
{
if (indexFrom > pagingRequest.PageNumber)
{
throw new ArgumentException(
$"indexFrom: {indexFrom} > pageNumber: {pagingRequest.PageNumber}, must indexFrom <= pageNumber");
}
var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
var items = source.Skip(((pagingRequest.PageNumber - 1) - indexFrom) * pagingRequest.PageSize)
.Take(pagingRequest.PageSize);
var props = typeof(T).GetProperties();
PropertyInfo orderByProperty;
orderByProperty =
props.FirstOrDefault(x=>x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);
if (pagingRequest.OrderBy == "desc")
{
items = items.OrderBy(x => orderByProperty.GetValue(x));
}
else
{
items = items.OrderBy(x => orderByProperty.GetValue(x));
}
var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
var pagedList = new PagedList<T>
{
PageNumber = pagingRequest.PageNumber,
PageSize = pagingRequest.PageSize,
IndexFrom = indexFrom,
TotalCount = count,
Items = result,
TotalPages = (int) Math.Ceiling(count / (double) pagingRequest.PageSize)
};
return pagedList;
}
but the result variable create exception
.OrderBy() requires a delegate that would tell it HOW to select a key, not the key value itself. So you are looking at some meta-programming here.
Naturally, you will look at building a dynamic LINQ Expression tree that will fetch a property for you:
// your code up above
PropertyInfo orderByProperty = props.FirstOrDefault(x => x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);
var p = Expression.Parameter(typeof(T), "x"); // you define your delegate parameter here
var accessor = Expression.Property(p, orderByProperty.GetMethod); // this basically becomes your `x => x.BranchId` construct
var predicate = Expression.Lambda(accessor, p).Compile(); // here's our problem: as we don't know resulting type at compile time we can't go `Expression.Lambda<T, long>(accessor, p)` here
if (pagingRequest.OrderBy == "desc")
{
items = items.OrderByDescending(x => predicate(x)); // passing a Delegate here will not work as OrderBy requires Func<T, TKey>
}
else
{
items = items.OrderBy(x => predicate(x)); // passing a Delegate here will not work as OrderBy requires Func<T, TKey>
}
var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
// your code down below
Problem with the above code - you don't know TKey upfront. Therefore we will have to go a level deeper and build the whole items.OrderBy(x => x.BranchId) expression dynamically. The biggest leap of faith here will be the fact the OrderBy is an extension method and it actually resides on IQueryable type. After you've got the generic method reference, you will need to build a specific delegate type when you know your property type. So your method becomes something like this:
public static class ExtToPagedListAsync
{
private static readonly MethodInfo OrderByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderBy" && method.GetParameters().Length == 2); // you need your method reference, might as well find it once
private static readonly MethodInfo OrderByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2); // you need your method reference, might as well find it once
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source, GeneralPagingRequest pagingRequest, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken))
{
if (indexFrom > pagingRequest.PageNumber)
{
throw new ArgumentException(
$"indexFrom: {indexFrom} > pageNumber: {pagingRequest.PageNumber}, must indexFrom <= pageNumber");
}
var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
var items = source.Skip(((pagingRequest.PageNumber - 1) - indexFrom) * pagingRequest.PageSize)
.Take(pagingRequest.PageSize);
var props = typeof(T).GetProperties();
PropertyInfo orderByProperty = props.FirstOrDefault(x => x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);
var p = Expression.Parameter(typeof(T), "x");
var accessor = Expression.Property(p, orderByProperty.GetMethod);
var predicate = Expression.Lambda(accessor, p); // notice, we're not yet compiling the predicate. we still want an Expression here
// grab the correct method depending on your condition
MethodInfo genericMethod = (pagingRequest.OrderBy == "desc") ? OrderByDescendingMethod.MakeGenericMethod(typeof(T), orderByProperty.PropertyType)
:OrderByMethod.MakeGenericMethod(typeof(T), orderByProperty.PropertyType);
object ret = genericMethod.Invoke(null, new object[] { items, predicate });
items = (IQueryable<T>)ret; // finally cast it back to Queryable with your known T
var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
var pagedList = new PagedList<T>
{
PageNumber = pagingRequest.PageNumber,
PageSize = pagingRequest.PageSize,
IndexFrom = indexFrom,
TotalCount = count,
Items = result,
TotalPages = (int)Math.Ceiling(count / (double)pagingRequest.PageSize)
};
return pagedList;
}
}
I must disclose I did get some inspiration from this answer here, so do check it out for further reading.

Stuck with an query that getting async error

I have a controller wich contains follows:
List<Game> gamesRat = new List<Game>();
if (selectedRating.Count() != 0)//User select any checkboxes
{
FilterGamesByRating(gamesRat, selectedRating, "Shooter");//Filter by user inputs
my = true;//Boolean
}
So the my question is:
when i try to return a View:
return View(await PaginatedList<Game>.CreateAsync(gamesRat.AsQueryable(), page ?? 1, pSize));
I get the following error:
The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations
But when in other method, i put that query:
IQueryable<Game> games = _context.Games.Where(x => x.Category == "Shooter");
return View(await PaginatedList<Game>.CreateAsync(games, page ?? 1, pSize));
It works perfectly. How can i fix that error? Thanks.
Of course, my PaginatedList method:
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage
{
get
{
return (PageIndex > 1);
}
}
public bool HasNextPage
{
get
{
return (PageIndex < TotalPages);
}
}
public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}

Cannot access array of a custom class

In unity I keep getting the error message "NullReferenceException: Object reference not set to an instance of an object" on this:
listOfBanks[0].Deposit(50);
and
accntBlnce.text = "Account Balance:\n" + listOfBanks[curBank].GetBalance().ToString("c");
I have 3 options listed in the drop down menu and when I Debug.Log the number of items in the array I get 3 as my count. But I can't do anything with them. The banks variable is set as the Dropdown object in the inspector as well as the accntBlnce as the text object in my panel.
The code is below.
Banks.cs
public class Banks : MonoBehaviour {
public Dropdown banks;
public Text accntBlnce;
public Bank[] listOfBanks;
public int curBank = 0;
void Start() {
listOfBanks = new Bank[banks.options.Count];
listOfBanks[0].Deposit(50);
}
void Update() {
curBank = banks.value;
accntBlnce.text = "Account Balance:\n" + listOfBanks[curBank].GetBalance().ToString("c");
}
}
Bank.cs
public class Bank{
public Bank() { }
public Bank(string orgn, float amntToRprt, float blnce) {
origin = orgn;
amountToReport = amntToRprt;
balance = blnce;
}
public string origin { get; set; }
public float amountToReport { get; set; }
public float balance { get; set; }
public bool Deposit(float amnt) {
if (amnt > 0) {
balance += amnt;
if(amnt > amountToReport) {
FlagForReport();
}
return true;
}
else
return false;
}
private void FlagForReport() {
throw new NotImplementedException();
}
public float GetBalance() {
return balance;
}
public bool Withdraw(float amnt) {
if (amnt > 0) {
if (balance >= amnt) {
balance -= amnt;
return true;
}
else
return false;
}
else
return false;
}
public bool Transfer(float amnt, Bank bank) {
if (amnt > 0) {
if (balance >= amnt) {
if(bank.Deposit(amnt))
balance -= amnt;
return true;
}
else
return false;
}
else
return false;
}
}
This is the fourth time array question is asked this week with the-same problem and the-same solution.
You declared the array here:
listOfBanks = new Bank[banks.options.Count];
but you did not create new instance of each Bank script before calling
listOfBanks[0].Deposit(50); and listOfBanks[curBank].GetBalance().ToString("c").
Declaring array and setting the size is NOT the-same as creating new instance of a script.
The solution is to loop through the array and create new instance of each one.
In your Banks.cs, replace the code in your Start() function with the one below:
void Start()
{
//Declare how much Bank array should be created
listOfBanks = new Bank[banks.options.Count];
//Now Create instance of each bank
for (int i = 0; i < listOfBanks.Length; i++)
{
//Create new instance of each Bank class
//listOfBanks[i] = new Bank();
listOfBanks[i] = new Bank("", 50, 50);
}
listOfBanks[0].Deposit(50);
}

Reactive Extensions Group By, Unique BufferWindow till time span with cancellation

I have a Hot stream of events coming of following type:
Event
{
string name;
int state ; // its 1 or 2 ie active or unactive
}
there is a function which provides parent name of given name - string GetParent(string name)
I need to buffer event per parent for 2 minutes, if during this 2 minute , i recv any event for child with state =2 for a given parent , this buffer should cancel and should output 0 otherwise i get the count of the events recvd .
I know I have to use GroupBy to partition, and then buffer and then count but i am unable to think of a way by which i create Buffer which is unique per parent, i though of using Distinct but this doesnt solve the problem, for i only dont want to create buffer till the parent is active (as once the parent's buffer gets cancelled or 2 minutes is over, the parent buffer can be created again)
So I understand I need to create a custom buffer which checks the condition for creating buffer, but how do i do this via reactive extensions.
Any help will be greatly appreciated.
regards
Thanks Brandon for your help. This is the main program I am using for testing. Its not working.As I am new to reactive extension problem can be in the way i am testing
namespace TestReactive
{
class Program
{
static int abc = 1;
static void Main(string[] args)
{
Subject<AEvent> memberAdded = new Subject<AEvent>();
//ISubject<AEvent, AEvent> syncedSubject = new ISubject<AEvent, AEvent>();
var timer = new Timer { Interval = 5 };
timer.Enabled = true;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, memberAdded);
var bc = memberAdded.Subscribe();
var cdc = memberAdded.GroupBy(e => e.parent)
.SelectMany(parentGroup =>
{
var children = parentGroup.Publish().RefCount();
var inactiveChild = children.SkipWhile(c => c.state != 2).Take(1).Select(c => 0);
var timer1 = Observable.Timer(TimeSpan.FromSeconds(1));
var activeCount = children.TakeUntil(timer1).Count();
return Observable.Amb(activeCount, inactiveChild)
.Select(count => new { ParentName = parentGroup.Key, Count = count });
});
Observable.ForEachAsync(cdc, x => WriteMe("Dum Dum " + x.ParentName+x.Count));
// group.Dump("Dum");
Console.ReadKey();
}
static void WriteMe(string sb)
{
Console.WriteLine(sb);
}
static void MyElapsedMethod(object sender, ElapsedEventArgs e, Subject<AEvent> s)
{
AEvent ab = HelperMethods.GetAlarm();
Console.WriteLine(abc + " p =" + ab.parent + ", c = " + ab.name + " ,s = " + ab.state);
s.OnNext(ab);
}
}
}
public static AEvent GetAlarm()
{
if (gp> 4)
gp = 1;
if (p > 4)
p = 1;
if (c > 4)
c = 1;
AEvent a = new AEvent();
a.parent = "P" + gp + p;
a.name = "C" + gp + p + c;
if (containedKeys.ContainsKey(a.name))
{
a.state = containedKeys[a.name];
if (a.state == 1)
containedKeys[a.name] = 2;
else
containedKeys[a.name] = 1;
}
else
{
containedKeys.TryAdd(a.name, 1);
}
gp++; p++; c++;
return a;
}
So this method , generates a event for Parent at each tick. It generates event for parent P11,P22,P33,P44 with State =1 and then followed by events for Parent P11,P22,P33,P44 with State =2
I am using Observable.ForEach to print the result, I see its being called 4 times and after that its nothing, its like cancellation of group is not happening
Assuming that a two minute buffer for each group should open as soon as the first event for that group is seen, and close after two minutes or a zero state is seen, then I think the following works:
public static IObservable<EventCount> EventCountByParent(
this IObservable<Event> source, IScheduler scheduler)
{
return Observable.Create<EventCount>(observer => source.GroupByUntil(
evt => GetParent(evt.Name),
evt => evt,
group =>
#group.Where(evt => evt.State == 2)
.Merge(Observable.Timer(
TimeSpan.FromMinutes(2), scheduler).Select(_ => Event.Null)))
.SelectMany(
go =>
go.Aggregate(0, (acc, evt) => (evt.State == 2 ? 0 : acc + 1))
.Select(count => new EventCount(go.Key, count))).Subscribe(observer));
}
With EventCount (implementing equality overrides for testing) as:
public class EventCount
{
private readonly string _name;
private readonly int _count;
public EventCount(string name, int count)
{
_name = name;
_count = count;
}
public string Name { get { return _name; } }
public int Count { get { return _count; } }
public override string ToString()
{
return string.Format("Name: {0}, Count: {1}", _name, _count);
}
protected bool Equals(EventCount other)
{
return string.Equals(_name, other._name) && _count == other._count;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((EventCount) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_name != null ? _name.GetHashCode() : 0)*397) ^ _count;
}
}
}
And Event as:
public class Event
{
public static Event Null = new Event(string.Empty, 0);
private readonly string _name;
private readonly int _state;
public Event(string name, int state)
{
_name = name;
_state = state;
}
public string Name { get { return _name; } }
public int State { get { return _state; } }
}
I did a quick (i.e. not exhaustive) test with Rx-Testing:
public class EventCountByParentTests : ReactiveTest
{
private readonly TestScheduler _testScheduler;
public EventCountByParentTests()
{
_testScheduler = new TestScheduler();
}
[Fact]
public void IsCorrect()
{
var source = _testScheduler.CreateHotObservable(
OnNext(TimeSpan.FromSeconds(10).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(20).Ticks, new Event("B", 1)),
OnNext(TimeSpan.FromSeconds(30).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(40).Ticks, new Event("B", 1)),
OnNext(TimeSpan.FromSeconds(50).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(60).Ticks, new Event("B", 2)),
OnNext(TimeSpan.FromSeconds(70).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(140).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(150).Ticks, new Event("A", 1)));
var results = _testScheduler.CreateObserver<EventCount>();
var sut = source.EventCountByParent(_testScheduler).Subscribe(results);
_testScheduler.Start();
results.Messages.AssertEqual(
OnNext(TimeSpan.FromSeconds(60).Ticks, new EventCount("B", 0)),
OnNext(TimeSpan.FromSeconds(130).Ticks, new EventCount("A", 4)),
OnNext(TimeSpan.FromSeconds(260).Ticks, new EventCount("A", 2)));
}
}
something like....
source.GroupBy(e => GetParent(e.name))
.SelectMany(parentGroup =>
{
var children = parentGroup.Publish().RefCount();
var inactiveChild = children.SkipWhile(c => c.state != 2).Take(1).Select(c => 0);
var timer = Observable.Timer(TimeSpan.FromMinutes(2));
var activeCount = children.TakeUntil(timer).Count();
return Observable.Amb(activeCount, inactiveChild)
.Select(count => new { ParentName = parentGroup.Key, Count = count };
});
This will give you a sequence of { ParentName, Count } objects.

SimplePager row count is working incorrectly

I'm using SimplePager and I want to show 12 items (users) per page. My entire data set is 20 items.
The problem is that the first page (correctly) shows items 1-12, but the second page shows items 9-20. I want the second page to show items 13-20.
What's going wrong?
Here is my code:
CellTable<User> cellTable = new CellTable<User>();
SimplePager pager = new SimplePager(TextLocation.CENTER);
pager.setDisplay(cellTable);
pager.setPageSize(12);
ListDataProvider<User> dataProvider = new ListDataProvider<User>();<br>
dataProvider.setList(USERSList);
dataProvider.addDataDisplay(cellTable);
Thank you in advance!
Setting
setRangeLimited(false)
will always show a next page.
Instead, I have rangeLimited at true and I've overridden the following method for this :
#Override
public void setPageStart(int index) {
if (getDisplay() != null) {
Range range = getDisplay().getVisibleRange();
int pageSize = range.getLength();
// Removed the min to show fixed ranges
//if (isRangeLimited && display.isRowCountExact()) {
// index = Math.min(index, display.getRowCount() - pageSize);
//}
index = Math.max(0, index);
if (index != range.getStart()) {
getDisplay().setVisibleRange(index, pageSize);
}
}
}
Try setting:
setRangeLimited(false)
More details:
http://groups.google.com/group/google-web-toolkit/browse_thread/thread/45e77082b796281d/d5101729e83a74ff?lnk=gst&q=pager+last+page#d5101729e83a74ff
#fbfcn and #MasterUZ's solution works, with a few slight modifications to make it comply with GWT 2.4's SimplePager:
public class MySimplePager extends SimplePager {
public MySimplePager() {
this.setRangeLimited(true);
}
public MySimplePager(TextLocation location, Resources resources, boolean showFastForwardButton, int fastForwardRows, boolean showLastPageButton) {
super(location, resources, showFastForwardButton, fastForwardRows, showLastPageButton);
this.setRangeLimited(true);
}
public void setPageStart(int index) {
if (this.getDisplay() != null) {
Range range = getDisplay().getVisibleRange();
int pageSize = range.getLength();
if (!isRangeLimited() && getDisplay().isRowCountExact()) {
index = Math.min(index, getDisplay().getRowCount() - pageSize);
}
index = Math.max(0, index);
if (index != range.getStart()) {
getDisplay().setVisibleRange(index, pageSize);
}
}
}
}
import com.google.gwt.user.cellview.client.SimplePager;
import com.google.gwt.view.client.Range;
public class MySimplePager extends SimplePager {
public MySimplePager() {
this.setRangeLimited(true);
}
public MySimplePager(TextLocation location, Resources resources, boolean showFastForwardButton, int fastForwardRows, boolean showLastPageButton) {
super(location, resources, showFastForwardButton, fastForwardRows, showLastPageButton);
this.setRangeLimited(true);
}
#Override
public void setPageStart(int index) {
if (getDisplay() != null) {
Range range = getDisplay().getVisibleRange();
int pageSize = range.getLength();
if (!isRangeLimited() && getDisplay().isRowCountExact()) {
index = Math.min(index, getDisplay().getRowCount() - pageSize);
}
index = Math.max(0, index);
if (index != range.getStart()) {
getDisplay().setVisibleRange(index, pageSize);
}
}
}
}