Using AutoMapper and PagedList in MVC - asp.net-mvc

I've read an article that in Entity Framework, the query will be sent to database after we call .ToList(), Single(), or First()
I have thousands of data so rather than load all the data I'd like to return data in paged. So I'm using PagedList to create paging in MVC. If it doesn't wrong when we called for example products.ToPagedList(pageNumber, 10), it will take only 10 records of data, not the whole data. Am I right?
Next, I'm using automapper to map from entities to viewmodel.
List<ProductViewModel> productsVM = Mapper.Map<List<Product>, List<ProductViewModel>>(products);
return productsVM.ToPagedList(pageNumber, 10);
As you can see in the snippet code above, does it take only 10 records before called .ToPagedList()? If when we do mapping, it will call .ToList() inside, I think it will call all of the data from the database then return 10 records. How to trace it?

The easiest way to see what is going on at database level is to use Sql Server Profiler. Then you will be able to see the sql queries that the entity framework is executing.
If you are using Sql Express then you can use Sql Express Profiler to do the same thing.

No, it doesn't take 10 records before paged list. The way your code is shown, AutoMapper will cause a deferred execution of the query, before it reaches paged list, which means it will return all data (let's suppose, 1000 records). Then PagedList will properly retrieve 10 of the already materialized List, and recognize the total amount of record was 1000.
I think you want to filter 10 in database, which has better performance, so you should use PagedList in the IQueryable of your database entities like this:
List<Product> filteredProducts = dbContext.Products.OrderBy(p => p.ProductId).ToPagedList(pageNumber, 10);
return Mapper.Map<List<Product>, List<ProductViewModel>>(filteredProducts);
The OrderBy is mandatory for PagedList.
BE CAREFUL WITH AUTOMAPPER
Consider the following scenario. What if your Product entity had a child relationship with ProductReview (a ICollection<ProductReview>) like this:
public class ProductReview
{
public int ProductId { get; set; }
public string Description { get; set; }
public int ReviewerId { get; set; }
public double Score { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductReview> Reviews { get; set; }
}
...and your ProductViewModel had an int property ReviewsCount to show in your view?
When Automapper would map and transform your entities into view model, it would access the Reviews property of each Product in the List (let's suppose, 10 in your case), one by one, and get the Reviews.Count() to fill ReviewsCount in your ProductViewModel.
Considering my example, where I never eager loaded Reviews of products, if Lazy Load was on, AutoMapper would execute ten queries (one per product) to count Reviews. Count is a fast operation and ten products are just a few. but if instead of count you were actually mapping the ProductReview to a ProductReviewViewModel, this would be kinda heavy. If Lazy Load was turned off, we would get an exception, since Reviews would be null.
One possible solution, is to eager load all child you might need during mapping, like this:
List<Product> filteredProducts = dbContext.Products.Include("Reviews").OrderBy(p => p.ProductId).ToPagedList(pageNumber, 10);
return Mapper.Map<List<Product>, List<ProductViewModel>>(filteredProducts);
...so 10 products and their reviews would be retrieved in just one query, and no other queries would be executed by AutoMapper.
But.......I just need a count, do I really need to retrieve ALL Reviews just to avoid multiple queries?
Isn't it also heavy to load all reviews and all their expensive fields like Description which may have thousands of characters???
Yes, absolutely. Avoid mixing PagedList with AutoMapper for these scenarios.
Just do a projection like this:
List<Product> filteredProducts = dbContext.Products
.Select(p => new ProductViewModel
{
ProductId = p.ProductId,
ProductName = p.Name,
ProductDescription = p.Description,
ReviewsCount = p.Reviews.Count(),
ScoreAverage = p.Reviews.Select(r => r.Score).DefaultIfEmpty().Average()
})
.OrderBy(p => p.ProductId).ToPagedList(pageNumber, 10);
Now you are loading your 10 products, projecting them into ProductViewModel, calculating the Reviews count and score average, without retrieving all Reviews from database.
Of course there are scenarios where you might really need all child entities loaded/materialized, but other than that, projection ftw.
You can also put the Select() part inside an extension class, and encapsulate all your projections in extension methods, so you can reuse them like you would to with AutoMapper.
I'm not saying AutoMapper is evil and you shouldn't use it, I use it myself in some situations, you just need to use it when it's appropriate.
EDIT: AUTOMAPPER DOES SUPPORT PROJECTION
I found this question where #GertArnold explains the following about AutoMapper:
...the code base which added support for projections that get translated
into expressions and, finally, SQL
So be happy, just follow his suggestion.

Related

Is recursive query possible in LINQ to Entities

this is my first question and sorry about my weak language.
I've got a table like this model;
public class Menu
{
[Key]
public int ID {get;set;}
public int ParentID {get;set;}
public string MenuName {get;set;}
public int OrderNo {get;set;}
public bool isDisplayInMenu {get;set;} // Menu or just for Access Authority
}
and there are many rows about menu like this;
ID ParentID MenuName Order
--- --------- ------------- ------
1 0 Main.1 1 >> if ParentID==0 is Root
2 1 Sub.1.1 1
3 2 Sub.1.2 2
4 0 Main.2 2
5 4 Sub.2.1 1
6 4 Sub.2.2 2
I have got a second class to prepare menu tree;
public class MyMenu:Menu
{
public List<MyMenu> Childs { get;set;}
}
I need a linq query to get the result like this;
var result = (...linq..).ToList<MyMenu>();
I am using a recursive function to get childs but this take too much time for to get results.
How can I write a sentence to get all menu tree in one query?
UPDATE:
I want to store main menu in a table. And this table will use on access authority control for users. Some rows will display inside the menu, some ones will use only to get access authority.
In this situation, I need many times to get the table tree. The table tree will be created as the filtered user authorities. When get the tree, stored in session. but many sessions means much RAM. If is there any fast way to get menu tree from the sql when I need then I will not store in the session.
If you need to walk the entire tree, you should use a stored procedure. Entity Framework is particularly ill-suited for recursive relationships. You'll either need to issue N+1 queries for each level, or eagerly load a defined set of levels. For example, .Include("Childs.Childs.Childs"), would load three levels. However, this is going to create a monstrous query, and you'll still need to issue N+1 queries for any additional level you don't include at the start.
In SQL, you can use WITH to recursively walk the table, and it will be much quicker than anything Entity Framework can do. However, your result will be flattened, rather than the object graph you would get back from Entity Framework. For example:
DECLARE #Pad INT = (
SELECT MAX([Length])
FROM (
SELECT LEN([Order]) AS [Length] FROM [dbo].[Menus]
) x
);
WITH Tree ([Id], [ParentId], [Name], [Hierarchy]) AS
(
SELECT
[ID],
[ParentID],
[MenuName],
REPLICATE('0', #Pad - LEN([Order])) + CAST([Order] AS NVARCHAR(MAX))
FROM [dbo].[Menus]
WHERE [ParentID] = 0 -- root
UNION ALL
SELECT
Children.[ID],
Children.[ParentID],
Children.[MenuName],
Parent.[Hierarchy] + '.' + REPLICATE('0', #Pad - LEN(Children.[Order])) + CAST(Children.[Order] AS NVARCHAR(MAX)) AS [Hierarchy]
FROM [dbo].[Menus] Children
INNER JOIN Tree AS Parent
ON Parent.[ID] = Children.[ParentID]
)
SELECT
[ID],
[ParentID],
[MenuName]
FROM Tree
ORDER BY [Hierarchy]
That looks much more complicated than it is. In order to ensure that the items in the menu are ordered properly by parent and their position within that parent's tree, we need to create a hierarchical representation of the order to order by. I'm doing that here by creating a string in the form of 1.1.1, where essentially each item's order is appended to the end of the parent's hierarchy string. I'm also using REPLICATE to left-pad the order for each level, so you don't have issues common with string ordering of numbers, where something like 10 comes before 2, because it starts with 1. The #Pad declaration just gets the max length I need to pad based on the highest order number in the table. For example, if the max order was something like 123, then the value of #Pad would be 3, so that orders less than 123 would still be three characters (i.e. 001).
Once you get past all that, the rest of the SQL is pretty straight-forward. You simply select all the root items and then union all with all their child by walking the tree. This and joining in each new level. Finally, you select from this tree the information you need, ordered by the hierarchy ordering string we created.
At least for my trees, this query is acceptably quick, but could be somewhat slower than you might like if the complexity scales or there's a ton of menu items to deal with. It's not a bad idea to do some sort of caching of the tree, even with using this query. Personally, for something like a site nav, I'd recommend using a child action combined with OutputCache. You call the child action in your layout where the nav should appear, and it will either run the action to get the menu or retrieve the already created HTML from cache if it exists. If the menu is specific to individual users, then just make sure you vary by custom, and factor in the user's id or something in your custom string. You could also just memory cache the result of the query itself, but you might as well reduce the cost of generating the HTML, too, while your at it. However, storing it in the session should be avoided.
LINQ to Entities does not support recursive queries.
However, loading the whole tree stored in a database table is easy and efficient. There seem to be some myths from earlier version of Entity Framework, so let's demystify them.
All you need is to create a proper model and FK relationship:
Model:
public class Menu
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string MenuName { get; set; }
public int OrderNo { get; set; }
public bool isDisplayInMenu { get; set; }
public ICollection<Menu> Children { get; set; }
}
Fluent configuration:
modelBuilder.Entity<Menu>()
.HasMany(e => e.Children)
.WithOptional() // EF6
.WithOne() // EF Core
.HasForeignKey(e => e.ParentID);
The important change is that in order to setup such relationship, ParentID must be nullable, and root items should use null instead of 0.
Now, having the model, loading the whole tree is simple as that:
var tree = db.Menu.AsEnumerable().Where(e => e.ParentID == null).ToList();
With AsEnumerable() we ensure that when the query is executed, the whole table will be retrieved in memory with a simple non recursive SELECT SQL. Then we simply filter out the root items.
And that's all. At the end we have a list with root nodes with their children, grand children etc. populated!
How it works? No lazy, eager or explicit loading is needed/used. The whole magic is provided by the DbContext tracking and navigation property fix up system.
I will try something like this.
This query will take all menu records from the database and will create dictionary with ParentId for key and all menus for specific parent id as values.
// if you're pulling the data from database with EF
var map = (from menu in ctx.Menus.AsNoTracking()
group by menu.ParentId into g
select g).ToDictionary(x => x.Key, x => x.ToList());
Now we can iterate the parentIds and create MyMenu instances very easly
var menusWithChildren = new List<MyMenu>()
foreach(var parentId in map.Keys)
{
var menuWithChildren = new MyMenu { ... }
menuWithChildren.AddRange(map[parentId]);
}
Now you have list with the associations. This way you will have the children and parent associated by reference (no dublicate references across different nesting levels) But i wonder how do you define the roots, if you need to know them at all ? I don't know if this is suitable for you.
public class Menu
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string MenuName { get; set; }
public int OrderNo { get; set; }
public bool isDisplayInMenu { get; set; }
public Menu Parent { get; set; }
public ICollection<Menu> Children { get; set; }
}
menuConfiguration:
builder.HasMany(z => z.Children).WithOne(z => z.Parent).HasForeignKey(z => z.ParentId).OnDelete(DeleteBehavior.Cascade);
MenuService:
public Task<List<Menu>> GetListByChildren()
{
return _dbSet.AsNoTracking().Include(z => z.Children).Where(z => z.ParentId == null).ToListAsync();
}

Is there a more efficient way to query data and pass as a View Model List to a View?

I have an Item model mapping to the DB like so:
public class Item
{
public int ItemId { get; set; }
public DateTime Created { get; set; }
public string Title { get; set; }
}
To display lists of these items, I have created a ItemSummaryViewModel like so:
public class ItemSummaryViewModel
{
public int ItemId { get; set; }
public string Title { get; set; }
public ItemSummaryViewModel(Item item)
{
this.ItemId = item.ItemId;
this.Title = item.JobTitle + " " + item.Created.ToString("ddd d MMM HH:mm");
}
}
I have also created a class to take a List< Item > and return a List< ItemSummaryViewModels > like so:
public class ItemSummaryViewModelList : List<ItemSummaryViewModel>
{
public ItemSummaryViewModelList(List<Item> items)
{
foreach (Item i in items)
{
ItemSummaryViewModel itemSummary = new ItemSummaryViewModel(i);
this.Add(itemSummary);
}
}
}
Finally, we use the controller to pass the list into the View like so:
public ActionResult Index()
{
//IEnumerable<ItemSummaryViewModel> itemsummaries = new IEnumerable<ItemSummaryViewModel>();
List<Item> ListOfItems = db.Items.ToList();
ItemSummaryViewModelList ListOfItemViewModels = new ItemSummaryViewModelList(ListOfItems);
return View(ListOfItemViewModels);
}
My Questions Are:
Is there a more efficient or "best practice" way of doing this?
To transform the list of DB models (Item) into a list of displayable View Models (ItemSummaryViewModels), we currently iterate through each item in the list and transform them individually. Is there a more efficient way of doing this ?
Essentially we're querying the DB and assigning the data to a ViewModel for display as a list. I can't help feeling that I'm "going round the houses" a bit and that there might be a more efficient or "best practice way of doing this.
Is there a better way?
Thanks
Try using LINQ select:
List<ItemSummaryViewModel> results = items.Select(
x =>
new ItemSummaryViewModel
{
ItemId = x.ItemId,
Title = x.Title + " " + x.Created.ToString("ddd d MMM HH:mm")
}).ToList();
Put that list in your view model.
Regarding efficiency, I would not worry until you have found that the simplest to implement solution was overly slow in practice. Get it working first and then only optimise when actually necessary. Obviously in the example you give, there are opportunities to only query and convert the subset of Items that the view requires (may it is all, but maybe you are paging?)
Structurally, I think the academic and professionally correct answer would be to have one set of objects to represent your database entities, a second set to represent the "domain" or business objects, and a third set to represent the all of the MVC models. However, depending the exact scenario this could be simplified:
If there is a really close mapping between the business objects and the database entities, and it is very unlikely that the database is going to change significantly, then you could have a single class for both.
If you have a very simple set of views that map very cleanly onto your business objects, then perhaps you can use business objects as your models. Unless your views do nothing but splat raw business objects onto a web page, I think your models will normally need to be more complicated than your current example though.
For that specific case, I would agree with #CorrugatedAir and say you could just use a plain List rather than create your own List class, and if want to be simpler, you could just use List and skip creating the ItemSummaryViewModel class too.
But try and be consistent throughout the application - so if you find a situation where your database entities can't be used as business objects, then it is best to have a separate set in all instances and have mappers between them.
To answer the "best practice" part of your question:
More efficient way (architecturally) will be to use Unit of Work and the repository patterns. That way you decouple your views from your data source, making it more reusable, more testable, more readable, hence more maintainable along with other "more"s.
The article is very graphical and gives you real feel of why do you need to tear apart database access from the controller.
To answer the technical part of how to transform it in a less verbose way,
I'd use something called AutoMapper. Using it, your complex transformation instead of the loop you presented will look as something like this:
public ActionResult Index()
{
var dbList = db.Items.ToList();
var vmList = Mapper.Map<List<Item>, List<ItemSummaryViewModel>>(dbList);
return View(vmList);
}
You will also have to put this initialization somewhere in your App_Start configuration (if MVC 4) or in Global.asax.cs file:
Mapper.CreateMap<ListOfItems , ItemSummaryViewModelList>();
Mapper.AssertConfigurationIsValid();
You can read more about why use AutoMapper and how to use it AutoMapper: Getting Started
Hope this helps!

Eager Load from Entity Framework SP

Im trying to populate my domain models and child entities with 1 SQL Store Proceedure execution. Perhaps this is answered here. Im pretty certain it's not possible but I though I would throw the question out there to find possible work arounds.
I have quite complex domain models and im looking for a more efficient way of loading my data rather than query a customer and then lazy load its children. I have presented a simple example of what im trying to achive below;
public class Customer{
public int Id { get; set; }
public virtual Address Address { get; set; }
}
public class Address{
public int Id { get; set; }
}
var customer = this.Database.SqlQuery< Customer >("exec SP_Name")
I know in EF5 you can return multiple data contexts but im hopeful I can resolve muliple child entities.
I hope ive made sense. Im lacking alot of sleep so apologies if it doesn't. Following a sport in a timezone 10 hours behind makes it difficult! :(
Stored procedures in EF don't offer eager loading. They can only load single level of entities. You can either use stored procedure with multiple result sets as mentioned in linked article but that works only with EDMX and you must execute mapped function import instead of SqlQuery. You can also simply use eager loading with LINQ query instead of stored procedure to avoid lazy loading:
var customers = context.Set<Customer>()
.Include(c => c.Address)
.FirstOrDefault(c => c.Name == someName);

MVC Entity Framework Partial Class access DB for property value

I am using Entity Framework mapped to my database. I have a Basket model which can have many BasketItem models, and I have Promotions and Coupons models.
This is for eCommerce checkout functionality and I just don't understand how this will work, here goes:
Because my BasketItems have a foreign key relationship to the Basket if I want to sum up the Subtotal for my basket items in a partial class, I can do this:
public decimal Subtotal {
get {
return this.BasketItems.Sum(pb => pb.Subtotal);
}
}
This is helpful because I can use this inside a view, there's no mucking around with passing a DB context through and it's DRY, etc. etc.
Now I want to apply promotions or coupons to my Subtotal ideally I want it to look like this:
public decimal DiscountedSubtotal {
get {
decimal promotions_discount = 0;
decimal coupons_discount = 0;
return Subtotal - promotions_discount - coupons_discount;
}
}
But there is no access to Promotions or Coupons without either creating some crazy and unnecessary relationships in my database or some light hacking to get this functionality to work. I don't know what I should do to overcome this problem.
Solution 1:
public decimal DiscountedSubtotal(DatabaseEntities db) {
decimal promotions_discount = from p in db.Promotions
select p.Amount;
decimal coupons_discount = from c in db.Coupons
select c.Amount;
return Subtotal - promotions_discount - coupons_discount;
}
I don't want to use this in my View pages, plus I have to send through my context every time I want to use it.
Solution 2: (untested)
public List<Promotion> Promotions { get; set; }
public List<Coupon> Coupons { get; set; }
public Basket()
: base() {
DatabaseEntities db = new DatabaseEntities();
Promotions = db.Promotions.ToList();
Coupons = db.Coupons.ToList();
}
A bit of light hacking could provide me with references to promotions and coupons but i've had problems with creating new contexts before and I don't know if there is a better way to get me to the DiscountedSubtotal property I would ideally like.
So to sum up my question, I would like to know the best way to get a DiscountedSubtotal property.
Many thanks and apologies for such a long read :)
I think the problem here is that you're not really using a coherent architecture.
In most cases, you should have a business layer to handle this kind of logic. Then that business layer would have functions like CalculateDiscountForProduct() or CalculateNetPrice() that would go out to the database and retrieve the data you need to complete the business rule.
The business class would talk to a data layer that returns data objects. Your view only needs it's view model, which you populate from the business objects returned by your businesss layer.
A typical method might be:
public ActionResult Cart() {
var model = _cartService.GetCurrentCart(userid);
return View(model);
}
So when you apply a discount or coupon, you would call a method like _cartService.ApplyDiscount(model.DiscountCode); then return the new model back to the view.
You might do well to study the Mvc Music Store sample project, as it includes cart functionality and promo codes.
http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-1

EF Code First: How do I make a virtual collection private while still having it correctly create my database model?

I am using Code First to automatically generate my database, and this works perfectly, generating an Orders table and an OrderLines table as expected when I add some test data.
I have the following Order class:
public class Order
{
public int OrderID { get; set; }
public void AddItem(string productCode, int quantity)
{
var existingLine = OrderLines.FirstOrDefault(x => x.ProductOption.ProductCode == item.ProductCode);
if (existingLine == null)
OrderLines.Add(new OrderLine { ProductOption = item, Quantity = quantity });
else
existingLine.Quantity += quantity;
}
public void RemoveItem(string productCode)
{
OrderLines.Remove(OrderLines.Where(x => x.ProductOption.ProductCode == productCode).FirstOrDefault());
}
public virtual ICollection<OrderLine> OrderLines { get; set; }
public Order()
{
OrderLines = new List<OrderLine>();
}
}
What I really want is to encapsulate the OrderLines collection, making it impossible for consumers of the class to directly add and remove items to/from it (using the Add / Remove methods of ICollection) and instead forcing them to use my custom AddItem and RemoveItem methods.
Normally I could just make the collection private, but I can't do that because it needs to be virtual for EF to correctly create the OrderLines table/foreign keys.
This answer seems to suggest that making the property internal would do the trick, but I tried, and in that case no OrderLines table is created.
Is there any way that this can be accomplished, or should I have designed this differently somehow? Any help much appreciated!
Update
After a bit more searching, I found this question which is rather more clearly stated than mine; however, it's still unanswered. The poster does link to this post which seems to suggest it can't really be done in the way I'm thinking of, but does anyone have any more up-to-date information?
I don't know if it's possible to do what you are asking or not, but I'm not sure it's the best design. The problem that I am seeing is you are firmly integrating your business logic into your business entities, and I think this will turn into confusion down the road.
Take the following scenario under consideration. Say you have a new requirement where you want users to be able to remove all items from an order. The only way to do it with your entity is to create a new RemoveAllItems() method to your Order class which does that. Now say you have a new requirement to Remove all items from an order that are in a specific category. That then means that you have to add yet another method.
This causes really bloated classes, and there is one major issue you will come up with. If you (or another developer) want to look at an entity and determine it's data structure, you can't at a glance because it's so intertwined with business logic.
What I would suggest is that you keep your entities as pure data structures, keeping all their relationships public. Then you need to create a service layer, which can consist of small or big classes (however you want to organize them) that actually perform the business functions. So for example, you can have a OrderItemService class, which has methods for adding, editing, and removing items from an order. All your business logic is performed in this class, and you just have to enforce that only service classes are allowed to interact with db entities.
Now, if you are looking for how a particular business process is performed, you know to look in the service layer classes, and if you want to look at how a data structure or entity is organized, you look at the entity. This keeps everything clean and very mantainable.
I'm far from an expert on code first and I haven't tried the following but is it possible to use the ReadOnlyCollectionBase and create a read only list similar to this MSDN article?
Well what you can do is set your collection as private and make the relationship using fluent API in the OnModelCreating, as shown below, I don't know if this will work, just make a try:
public class YourContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLines { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasMany(o => o.OrderLines)
.WithRequired(l => l.OrderId)
.HasForeignKey(l => l.OrderId);
}
}
This will make your OrderLines as readonly:
public class YourContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLines
{
get { return set<OrderLine>(); }
}
}
I hope this can help you, please take a look a this blog post: EF Feature CTP5: Fluent API Samples

Resources