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
Related
I'm looking for some guidance in terms of MVC architecture in terms of where to put the following logic.
My question is: Where should I put the logic to calculate the product rating?
I want to display some details about a product, and the rating in stars. The star rating is made up by the average user rating and an internal rating; weighted at 50% each.
Star Rating = 0.5(InternalRating) + 0.5(AverageUserRating)
I currently use the Product controller to populate 3 fields on the ViewModel:
#for (int i = 1; i <= Model.FullStars; i++)
{
<i class="full-star"></i>
}
#if (Model.HalfStar == true)
{
<i class="half-star"></i>
}
#for (int i = 1; i <= Model.EmptyStars; i++)
{
<i class="empty-star"></i>
}
At the moment I loop through the UserRating result in the Produt controller to get the average rating, and the logic to split into full and empty stars lies there.
The problem with my approach is this logic has to be duplicated in various controllers which call the product.
In terms of my understanding, a good method would be to create a class:
public class ProductStars
{
public int FullStars { get; set; }
public bool HalfStar { get; set; }
public int EmptyStars { get; set; }
}
Then create a DisplayTemplate for this class. Is this correct? And if so-- would I move the logic into a separate method in a utility class which accepts the product ID, gets the ratings from the database and populates the class?
Should the logic to populate the class then perhaps be moved into a table-valued function in SQL?
Thanks.
Personally, I would move the ratings logic into a business logic service and have that injected into the controller via dependency injection, then create a stars rating view helper to encapsulate your HTML used for creating the stars.
This way you've encapsulated your rating business logic that could theoretically be called via a website or web service, and you've also encapsulated your HTML generation for the rating view.
This article does a good job at demonstrating how to create a custom view helper.
I would definitely recommend trying to keep your controllers as skinny as possible as highlighted in one of rsenna's earlier comments. The controller should really only be used for handling the request and talking between the view and model.
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.
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!
In my ASP.NET MVC project, my actions typically call a Service layer to get data. I use the same dozen or so POCOs for all my models. I also plan on using the Service layer in console applications and maybe expose a web api at some point.
To make my database operations more efficient, my service layer only hydrates the properties in the model that are relevant to the particular method (which at this point is mostly driven by the needs of my controller actions).
So for example I might have a class Order with properties Id, Name, Description, Amount, Items. For a given service call I might only need to populate Id, Name, Items. A consumer of that service won't necessarily know that Amount is 0 only because it didn't populate the property.
Similarly, the consumer won't know whether Items is empty b/c there actually aren't any items, or whether this particular service method just doesn't populate that property.
And for a third example, say one of my views displays an ItemCount. I don't want to fully populate my Items collection, I just need an additional property on my "model". I don't want to add this property to my POCO that other service methods will be using because it's not going to be populated anywhere else.
So the natural solution is to make a POCO designed specifically for that method with only those 3 properties. That way the consumer can know that all properties will be populated with its real values. The downside to this is that I'll end writing tons of similarly shaped models.
Any advice on which method works best?
You could use Nullable Types to indicate the missing properties with a null.
For example:
class Order {
public int Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public decimal? Amount {get;set;}
public List<Item> Items {get;set;}
}
And then if Items == null, it wasn't set. If it's an empty new List<Item>(), it's set but empty. Same for Amount. If Amount.HasValue == false, it wasn't set. If Amount.Value is 0.0d, it's set and the item is free.
Why don't you use LINQ projection?
One service method does something like:
return DbContext.Orders.Select(o => new { Id = o.Id, Name = o.Name, Description = o.Description });
while the other service method does something like:
return DbContext.Orders.Select(o => o);
I'm not sure how your application is architected, but this may be a way around creating 100's of POCO's.
Hope this helps! Good luck.
You could pass in a selector Func that returns dynamic:
public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector) { ... }
I'm not sure how you are accessing data, but the following shows how this would work using a List<T>:
class Program
{
static void Main(string[] args)
{
var service = new Service();
var orderNames = service.GetOrders(o => new { o.Name });
foreach (var name in orderNames)
Console.WriteLine(name.Name);
Console.ReadLine();
}
}
public class Service
{
private List<Order> _orders = new List<Order>
{
new Order { Id = 1, Name = "foo", Description = "test order 1", Amount = 1.23m },
new Order { Id = 2, Name = "bar", Description = "test order 1", Amount = 3.45m },
new Order { Id = 3, Name = "baz", Description = "test order 1", Amount = 5.67m }
};
public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector)
{
return _orders.Select(selector);
}
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Amount { get; set; }
}
The use of nullable values is a good solution, however it has the downside you have no way to matk required fields. That is you cannot use a required attribute on any property. So if there is field that is obligatory in some views you have no way to represent it.
If you don't need required fileds validation this is ok. Otherwise, you need a way to represent which fileds are actually used, and then to write a custom validation provider.
A simple way to do this is to use a "Mask" class with the same property names of the original class, but with all fields boolean: a true values means the field is in use.
I used a similar solution in a system where the properties to be shown are configured in a configuration files...so it was the unique option for me since I had no possibility to represent all combination of properties. HOWEVER, I used the "Mask" class also in the View, so I was able to do all the job with just one View..with a lot of ifs.
Now if your 150 service methods and probably about 150 Views...are all different, then maybe it is simpler to use also several classes ...that is in the worst case 150 classes..the extra work to write them is negligible if compared to the effort of preparing 150 different Views.
However this doesnt mean you need 150 POCO classes. You might use an unique POCO class that is copied into an adequate class just into the presentation Layer. The advantage of this approach is that you can put different validation attributes on the various classes and you don't need to write a custom Validation provider.
Return the entire POCO with nullable types as mentioned by #sbolm. You can then create a ViewModel per MVC page view that receives a model with the specific properties it needs. This will take more performance (insignificant) and code, but it keeps your service layer clean, and keeps your views "dumb" in that they are only given what they need and have no direct relation to the service layer.
I.e. (example class from #sbolm)
class Order {
public int Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public decimal? Amount {get;set;}
public List<Item> Items {get;set;}
}
// MVC View only needs to know the name and description, manually "map" the POCO properties into this view model and send it to the view
class OrderViewModel {
public string Name {get;set;}
public string Description {get;set;}
}
I would suggest that instead of modifying the models or creating wrapper models, you have to name the service methods such that they are self-explanatory and reveals the consumer what they returns.
The problem with the nullable approach is it makes the user to feel that the property is not required or mandatory and they try inserting instances of those types without setting those properties. Is it won't be bad having nullables every-where?
It won't be a good approach to change the domain models since all you want is just to populate some of the properties instead of that you create service with names and descriptions that are self-explanatory.
Take the Order class itself as the example, say one service method returns the Order with all the items and the other one returns only the details of the Order but not the items. Then obviously you may have to create two service methods GetOrderItems and GetOrderDetail, this sounds so simple, yes it is! but notice the service method names itself tells the client what it is going to return. In the GetOrderDetail you can return an empty items or null (but here I suggest a null) that doesn't matter much.
So for new cases you don't need to frequently change the models but all you got to do is add or remove the service methods and that's fine. Since you are creating a service you can create a strong documentation that says what method does what.
I would not performance optimize this to much unless you realy get performance problems.
I would only distinguish between returning a flat object and an object with a more complete object graph.
I would have methods returning flat objects called something like GetOrder, GetProduct.
If more complete object graphs are requested they would be called : GetOrderWithDetails.
Do you use the POCO classes for the typed views? If yes: try to make new classes that serve as dedicated ViewModels. These ViewModels would contain POCO classes. This will help you keeping the POCO classes clean.
To expand on the nullable idea, you could use the fluentvalidation library to still have validation on the types dependent on whether they are null or not. This would allow you to have a field be required as long as it was not null or any other validation scheme you can think of. Example from my own code as I had a similar requirement:
Imports FluentValidation
Public Class ParamViewModelValidator
Inherits AbstractValidator(Of ParamViewModel)
Public Sub New()
RuleFor(Function(x) x.TextBoxInput).NotEmpty.[When](Function(x) Not (IsNothing(x.TextBoxInput)))
RuleFor(Function(x) x.DropdownListInput).NotEmpty.[When](Function(x) Not (IsNothing(x.DropdownListInput)))
End Sub
End Class
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