There is a book management system web application based on ASP.NET MVC3. When click the book index page, data query is very slow and user has to wait for several seconds for response. The code of Action Index in BookController below:
public ViewResult Index(string sortOrder, int? page)
{
ViewBag.NameSortParam = string.IsNullOrEmpty(sortOrder) ? "desc" : "";
ViewBag.CurrentSort = sortOrder;
BookModel books = from b in db.Books select b; // db = new BookContext();
switch (sortOrder) {
case "desc":
books = books.OrderByDescending(b => b.Name);
break;
default:
books = books.OrderBy(b => b.Name);
break;
}
int pageSize = 15;
int pageNumber = (page ?? 1);
return View(books.ToPagedList(pageNumber, pageSize));
}
In my opinion, the main reason of slow is that server does not response to client until all data are ready. This process takes much time.
I do not know how to solve this problem, is there any method to improve data query performance in this case? Thank you!
update:
backend database is SQL Server Compact Edition 4.0
UPDATE
I update my logic code below, orderby is used before skip and take statements. Everything goes well. Thank you all for help.
books = (from b in db.Books
orderby b.Name descending
select b)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
You are loading in all your books before filtering the dataset to just the data you need (by using ToPagedList). Change your query and use the Skip and Take methods instead of ToPagedList.
Put index on the Books.Name column (with included columns: include those which are needed in your list), on the C# side query only the filed you are needed:
books.Select(x => new {
x.Name,
x.Author,
x.Description,
x.ID }).ToPagedList(/*....*/)./*...*/)
And i would look after the ToPagedList implementation.
Use OutputCaching on the method, varying by the two parameters. An overview of this is detailed here.
[OutputCache(Duration=60,VaryByParam="sortOrder;page;")]
public ViewResult Index(string sortOrder, int? page)
Related
I have hit a small issue and hoping someone might be able to assist. I am using a panel - On the page load, it should list all the products as no category has been selected as per stored procedure (this works perfectly).
When a user clicks on a specific category, it should only show the products that have the specific CategoryID. When I run the code in SQL, it works a dream for this part too, so assume the stored procedure is ok.
At
CategoryID = CategoryID
in GetProducts, I get
Warning: Assignment made to same variable; did you mean to assign something else?
However I am following a tutorial video and this works fine. Is there another silly error that is preventing it from working?
I think I have included all the required code - sorry if its a bit overkill!!
Thanks as ever in advance - Jack
Code behind pnlCategories:
private void GetProducts(int CategoryID)
{
ShoppingCart k = new ShoppingCart();
{
CategoryID = CategoryID;
};
Error identified - additional ";" added at following line:
ShoppingCart k = new ShoppingCart();
Code now reads
ShoppingCart k = new ShoppingCart()
{
CategoryID = CategoryID
};
and functions as expected!
that looks like a c# error and not a SQL Server error.
The problem is here in your GetProducts method. CategoryID = CategoryID;
C# is case sensitive. If you check your tutorial carefully, one of these will probably be lower case. Make sure you type that carefully.
try code change below and see where the compiler complains.
CategoryID = categoryID;
private void GetProducts(int CategoryID)
{
ShoppingCart k = new ShoppingCart();
{
CategoryID = CategoryID;
};
dlProducts.DataSource = null;
dlProducts.DataSource = k.GetProdcuts();
dlProducts.DataBind();
}
I have wrote this code to see details in details view with petapoco. but it is not showing any data and it also showing null parameter. I have added and details view page.. here my database name FCBook and table is RMReceive and primary key is RrId.. please help to run this code successfully...
public ActionResult Details(int id)
{
var db = new PetaPoco.Database("FCBook");
var rmr = db.Single<RMReceive>("select * from RMReceive where RrId= #0",id);
return View(rmr);
}
You are using an expression here, your query probably won't work with raw SQL, try
var rmr = db.Single<RMReceive>(x => x.RrId == id);
Morning,
I would like to know how to write the following SQL statement in LINQ.
SELECT TOP 6 * FROM Questions
ORDER BY NEWID()
I would also like to know, how i could bind this to a asp.net repeater control to display the 6 questions.
Many thanks :)
The Linq style would be
Questions.OrderBy(q=>Guid.NewGuid()).Take(6)
then you attach that to a repeater by setting its DataSource property to the above, and calling the DataBind method.
You would have to be able to invoke the NEWID() function to generate your random guids. To do so, you could take some hints here and first create a pseudo-method mapped to the NEWID() function on your data context.
[System.Data.Linq.Mapping.Function(Name="NEWID", IsComposable=true)]
public Guid NewId()
{
throw new NotImplementedException();
}
Once that is set, you could then write your query to use this function:
var query = dc.Questions
.OrderBy(question => dc.NewId())
.Take(6);
You can inspect the SQL query generated for this and it should match.
Questions.OrderBy(q=>Sql.NewGuid()).Take(6)
This will invoke the NEWID() in SQL statement.
(from db in context.Questions
order by Guid.NewGuid()
select db).Take(6);
I know answer is already selected, but still I'm adding my way to achieve this. Faced same situation today and tried couple of ways, used questions.OrderBy(q => Guid.NewGuid()).ToList() and couple of more suggestions. Later I thought to add a new field string RandomOrder in view model and assigned Guid.NewGuid().ToString() in loop and then used questions.OrderBy(i => i.RandomOrder).ToList() and this worked great.
I had requirement to shuffle questions if author selected option shuffleAlways while creating assessment. If not then sort on regular sorting order. Here is complete solution:
private List<AssessmentQuestionsViewModel> LoadAllQuestions(string assessmentId, bool shuffleQuestions)
{
List<AssessmentQuestionsViewModel> questions = new List<AssessmentQuestionsViewModel>();
var items = assessmentQuestionRepository.GetAll().Where(i => i.AssessmentId == assessmentId).ToList();
foreach (var item in items)
{
questions.Add(new AssessmentQuestionsViewModel
{
Id = item.Id,
AssessmentId = item.AssessmentId,
QuestionText = item.QuestionText,
HintText = item.HintText,
QuestionType = item.QuestionType,
MaxMarks = item.MaxMarks,
SortOrder = item.SortOrder,
RandomOrder = Guid.NewGuid().ToString(),
Answers = LoadAllAnswers(item.Id)
});
}
if (shuffleQuestions)
{
questions = questions.OrderBy(i => i.RandomOrder).ToList();
}
else
{
questions = questions.OrderBy(i => i.SortOrder).ToList();
}
return questions;
}
And this worked like charm. Hope this help others.
I assume you are using ORDER BY NEWID() as a way to select random data from your questions? If so, you should avoid using NEWID() (or it's LINQ equivalent), causes tons a new guid to be generated for every record in your table. On a large dataset, that's devestating.
Instead, see Linq Orderby random ThreadSafe for use in ASP.NET for an optimized solution to random sorts. Then just add a take operator and your set.
Random random = new Random();
int seed = random.Next();
var RandomQuestions = Questions.OrderBy( s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
return RandomQuestions.Take(6);
OK, this thing just puzzles me.
I have a table, say Users, with columns UserID, Name, etc. Have an object mapped to it using CTP5. So now I want to test it, and do the following:
List<User> users = new List();
// Some init code here, making say 3 users.
using (UsersDbContext)
{
// insert users
}
So far so good, works fine.
Now I want to see if the records match, so I select the users back using the following code.
using (UsersDbContext dbc = UsersDbContext.GetDbContext())
{
List<Users> usersRead = dbc.Users.Where(x => x.ID >= users[0].ID && x.ID <= users[users.Count - 1].ID).ToList();
}
This throws and exception:
System.NotSupportedException: LINQ to
Entities does not recognize the method
'User get_Item(Int32)' method, and
this method cannot be translated into
a store expression.
EF has difficulties seeing that I'm just asking to return an int in Users[0].ID ?
If I replace a call to users[0].ID with a straight int - works fine.
I get what it's trying to do, but I thought it should be pretty easy to check if the method belongs to .NET or Sql Server ?
You are trying to access an indexer in an EF expression, which doesn't translate to an SQL query. You'll have to move the parameters outside the query like this:
int first = users[0].ID;
int last = users[users.Count - 1].ID;
List<Users> usersRead = dbc.Users.Where(x => x.ID >= first && x.ID <= last).ToList();
My problem is that I am trying to return a simple query that contains an object Story. The Story object has a UserId in the table which links to aspnet_users' UserId column. I have created a partial class for Story that adds the UserName property since it does not exist in the table itself.
The following query gets all stories; however, a pagination helper takes the query and returns only what's necessary once this is passed back to the controller.
public IQueryable<Story> FindAllStories(){
var stories = (from s in db.Stories
orderby s.DateEntered descending
select new Story
{
Title = s.Title,
StoryContent = s.StoryContent,
DateEntered = s.DateEntered,
DateUpdated = s.DateUpdated,
UserName = s.aspnet_User.UserName
}
);
return stories;
}
When the helper does a .count() on the source it bombs with the following exception:
"Explicit construction of entity type 'MyWebsite.Models.Story' in query is not allowed."
Any ideas? It's not a problem with the helper because I had this working when I simply had the UserName inside the Story table. And on a side note - any book recommendations for getting up to speed on LINQ to SQL? It's really kicking my butt. Thanks.
The problem is precisely what it tells you: you're not allowed to use new Story as the result of your query. Use an anonymous type instead (by omitting Story after new). If you still want Story, you can remap it later in LINQ to Objects:
var stories = from s in db.Stories
orderby s.DateEntered descending
select new
{
Title = s.Title,
StoryContent = s.StoryContent,
DateEntered = s.DateEntered,
DateUpdated = s.DateUpdated,
UserName = s.aspnet_User.UserName
};
stories = from s in stories.AsEnumerable() // L2O
select new Story
{
Title = s.Title,
StoryContent = s.StoryContent,
...
};
If you really need to return an IQueryable from your method and still need the Username of the user you can use DataContext.LoadOptions to eagerload your aspnet_user objects.
See this example.