public class MyDbContext: DbContext
{
}
public class Product
{
public long Id {get; set;}
public long CategoryId {get; set;}
[ForeignKey("CategoryId")]
public virtual Category Category {get; set;}
}
public class Category
{
public long Id {get; set;}
public string Name {get; set;}
}
List<Product> GetProducts()
{
var context = new MyDbContext();
var products = context.Set<Product>().ToList();
var categories = context.Set<Category>().ToList();
foreach(var product = in products)
{
product.Category = categories.First(c => c.Id == product.CategoryId);
}
return products;
}
Here I want to retrieve all products with associated categories with best performance.
I first tried with lazy loading, but it results in many database queries. Then I use eager loading, but the query scripts generated are not so efficient especially for complex query. So I used the following way:
get all Products,
get all Categories and
set the navigation property "Category" of a product manually from the fetched categories
My questions are:
-Will EF still lazy load the navigation property "Category" even after I set it manually?
-Is there any better solution to eager loading for complex query?
You get the best result if you use Include:
var products = context.Set<Product>().Include("Category");
This will load products and their categories in one query and the Category properties will not trigger lazy loading any more.
Even better is the extension method Include: context.Set<Product>().Include(p => p.Category);.
Related
I'm new to MVC and am used to using Forms. My question is suppose I have basic models setup for four tables with the following fields;
Branch
------
Branch_Nu
Branch_Address
Orders
-----
Order_Nu
Branch_Nu
Product_Nu
Customer_Nu
totcost
Products
---------
Product_Nu
Product
Price
Customer
----------
Customer_Nu
Name
Address
City
St
Zip
I'm interested in the following scenarios,
I want to see all orders for a branch;
Branch->orders
I want to see all orders for a customer from a particular branch;
Branch->orders->products
|-->customer
I want to see all orders for a customer regardless of branch they
purchased from;
customer->orders->branch
I want to see all branches that sold a particular product;
products->orders->Branch
I want to see which customers bought a particular product;
products->orders->customer
Question is can I use different controllers for the different scenarios using the same basic models that is submitted to different controller methods, or do I need different models for the different scenarios which is then submitted to different controller methods?
If I were using forms I would just have a different select statements and forms for each scenario, in MVC?
your entity framework models represent the relationships between your classes. In a code first approach, your models will dictate the design of your database. Take the following three classes for example:
public class Cat
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
//Foreign key to animalgroup table
public int AnimalGroupId {get; set;}
//navigation property to AnimalGroup
//allows you to do myCat.AnimalGroup outside of this class to retrieve
//the associated animal group
public virtual AnimalGroup AnimalGroup {get; set;}
}
public class Dog
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
public bool IsFluffy {get; set;}
//Foreign key to animalgroup table
public int AnimalGroupId {get; set;}
//navigation property to AnimalGroup
//allows you to do myDog.AnimalGroup outside of this class to retrieve
//the associated animal group
public virtual AnimalGroup AnimalGroup {get; set;}
}
public class AnimalGroup
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<Cat> Cats {get; set;}
public virtual ICollection<Dog> Dogs {get; set;
}
this represents two one-to-many relationships. A AnimalGroup can contain multiple Cats and an AnimalGroup can contain many Dogs. You can write queries to do CRUD operations. A very simplistic example is the following:
//create and save an animal group
AnimalGroup group = new AnimalGroup();
group.Name = "my animal group";
_dbContext.AnimalGroups.Add(group);
_dbContext.SaveChanges();
//create and save a cat associated with the animal group
Cat myCat = new Cat();
cat.Name = "kitty";
cat.AnimalGroupId = group.Id;
_dbContext.Cats.Add(myCat);
_dbContext.SaveChanges();
Question is can I use different controllers for the different scenarios using the same basic models
Yes
do I need different models for the different scenarios which is then submitted to different controller methods
You don't need them, but generally speaking using a model for a different logical function tends (not always) to break the Single Responsibility Principle.
In your instance it appears you are viewing the same data by different views (not logic). An order is an order, for viewing purposes anywhere on your site, generally I use the same model. Placing an order (different logic function) I would most likely have a new model.
Considering you are viewing the same data this is a classic example of the best use of MVC Templates.
If I were using forms I would just have a different select statements and forms for each scenario, in MVC?
I would probably design it like:
I want to see all orders for a branch;
Branch->orders
public class BranchController() { public ActionResult Orders() {}}
I want to see all orders for a customer from a particular branch;
Branch->orders->products
|-->customer
public class BranchController()
{
public ActionResult Orders() {}
public ActionResult CustomerOrders() {}
public ActionResult ProductOrders() {}
}
// ETC
A model example:
public class OrderVM
{
//I would get rid of hungarian notation
// https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation
public int OrderId { get; set; }
public int BranchId { get; set; }
public int ProductId { get; set; }
public int CustomerId { get; set; }
}
Then store how the html is rendered in /views/shared/templates/display/OrderVM.cshtml so it can be used throughout the application, allowing overrides per controller/area as well.
I am building a application using Entity Framework 6.1 code first and ASP.NET MVC5.
I got the following model:
class Person {
public Int32 Id {get; set;}
[Required]
public String Name {get; set;}
public List<Book> Books {get; set;}
}
class Book {
public Int32 Id {get; set;}
[Required]
public String Title {get; set;}
public List<Person> Authors {get; set;}
}
The two basic edit-views for changing Person.Name or Book.Title are straight forward.
Now I got one "real world"-view where the user can edit a Book and add existing Authors to them.
The view posts the values like this:
Title = "Nice Book"
Authors[0].Id = 7
Authors[1].Id = 3
The view will only send the Id of the Persons/Authors. After all, the user won't rename an author while editing a Book.
All the model binding works great and the controller can map the posted fields.
Yet the validation of the bound model fails when the author/Person has no (required) Name set.
Is there some kind of workaround? Currently I omit the RequiredAttribute for the affected properties. I would like to not make a view-model for every aspect of my (rather complex) model-graph where some or another item will get associated.
This is why you should use view models. As they allow you to customize what properties should be exposed to the view. However, you're also no handling the posting of selected authors correctly. For a multiselect like this you really need a view model.
public class BookViewModel
{
public BookViewModel()
{
SelectedAuthorIds = new List<int>();
}
[Required]
public string Title { get; set; }
public List<int> SelectedAuthorIds { get; set; }
public IEnumerable<SelectListItem> AuthorChoices { get; set; }
}
Notice, first, that there's no Id property. You may need to add this if you want to use this same view model to edit books, as well, but for the purposes of creation, there's no need to have an Id field in the view, so we leave it off. Next, there's no Authors property. Instead we have SelectedAuthorIds which is a list of integers and AuthorChoices which will hold all the Authors a user can select from. Finally, there's a constructor to initialize SelectedAuthorIds so it's never null. An empty list translates to nothing being selected, but we need an actual list.
Now, use this view model as the model for your view:
#model Namespace.To.BookViewModel
And, change your author select field(s) to:
#Html.ListBoxFor(m => m.SelectedAuthorIds, Model.AuthorChoices)
You'll also need to populate AuthorChoices, which you'll handle in your GET action for this form:
public ActionResult Create()
{
var model = new BookViewModel();
model.AuthorChoices = db.Authors.Select(m => new SelectListItem
{
Value = m.Id.ToString(),
Text = m.Name
});
return View(model);
}
Finally, after the user posts the form, you'll need to use SelectedAuthorIds to fetch those authors from the database and add them to your Book being created. As far as that goes, you'll also need to map the data from BookViewModel to an instance of Book. All this means is copying over the data from one to the other. There's libraries for this type of thing, such as AutoMapper, but this is relatively simple, so I'll handle it manually:
[HttpPost]
public ActionResult Create(BookViewModel model)
{
if (ModelState.IsValid)
{
var book = new Book
{
Title = model.Title
};
book.Authors = db.Authors.Where(m => model.SelectedAuthorIds.Contains(m.Id));
db.Books.add(book);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
The edit version will be similar, except you'll instead pull an existing Book instead of creating a new one:
[HttpPost]
public ActionResult Edit(int id, BookViewModel model)
{
var book = db.Books.Find(id);
book.Title = model.Title;
...
}
I've tinkered with derived classes, interfaces and viewmodels, but I haven't been able to create quite what I need.
Say we're building a CMS with the following models:
ArticleItem
Title
Summary
Content
NewsItem
Headline
PublishDate
Summary
Content
EventItem
EventTitle
StartDate
EndDate
Content
I'm looking for a way to standardise the display of these into one format / view (e.g. so we can display them all in the same RSS feed). The standardized view might be called HTMLItem and have 3 fields:
Title
Summary
Content
The ArticleItem would translate directly to the HTMLItem, that's straightforward.
For the NewsItem I would like to join the PublishDate and the first 100 characters of the content to create Summary field of HTMLItem.
For the EventItem I would like to combine the StartDate and EndDate to create the Summary field of HTMLItem.
Ultimately I'm looking for the easiest, most efficient way to be able to pass the 3 models into a single view that has been designed to display HTMLItem. My best shot so far has been to create a 'convertor' class for each model, but I can't help feeling that there is a better way to do this.
Any experience, expertise and advice would be much appreciated!
Make a ViewModel with the standarized properties and a constructor for each specialized class:
public class HtmlItemViewModel {
//Properties
public string Title {get; set;}
public string Summary {get; set;}
public string Content {get; set;}
//Constructor inside HtmlItemViewModel for each one of the specialized classes:
public HtmlItemViewModel(ArticleItem item)
{
this.Title = item.Title;
this.Summary = item.Summary;
this.Content = item.Content;
}
public HtmlItemViewModel(NewsItem item)
{
this.Title = item.Headline;
this.Summary = String.Format("{0} - {1}", item.PublishDate, item.Summary.Substring(0,1000));
this.Content = item.Content;
}
public HtmlItemViewModel(EventItem item)
{
this.Title = item.EventTitle;
this.Summary = String.Format("{0} - {1}", item.StartDate, item.EndDate);
this.Content = item.Content;
}
}
Then, on the method you use for your RSS Feed simply pass each entity to the constructor on each individual query. Like this:
//Example controller
public class RssController : Controller {
public ActionResult GetRssFeed(){
//Assuming you have a service for each item type
var articleList = ArticleService.GetArticles().Select(s => new HtmlItemViewModel(s));
var newsItemList = NewsItemService.GetNewsItems().Select(s => new HtmlItemViewModel(s));
var eventItemList = EventItemService.GetEvents().Select(s => new HtmlItemViewModel(s));
articleList.AddRange(newsItemList);
articleList.AddRange(eventItemList);
return articleList;
}
}
You can use Viewmodel Pattern in your project
Models and ViewModels are different. Don't confuse the ViewModel with
the MVVM pattern.
The use of a view model can make the interaction between model and
view more simple. A model can sometimes be over complicated having
other model objects as members, which could have model objects as
member etc..
By using a view model you have a good way to simplify what the view
deals with. This will also filter down what can be seen in
intellisense, so if you have different people developing the models
than those working on the views, creating a simple view model can make
it much easier for those just dealing with the UI.
The simple and most common solution to this is to create a composite view model class. This can be a composed class (containing references to your domain models), or a flattened class, referencing properties from each class individually.
So you could do this:
public class HtmlItemViewModel
{
public ArticleItem ArticleItem {get; set;}
public NewsItem NewsItem {get; set;}
public EventItem EventItem {get; set;}
}
Or this:
public class HtmlItemViewModel
{
//Article Item Properties
public string ArticleTitle {get; set;}
public string ArticleContent {get; set;}
public string ArticleSummary {get; set;}
//News Item Properties
public string Headline {get; set;}
public DateTime PublishDate {get; set;}
public string NewsItemSummary {get; set;}
public string NewsItemContent {get; set;}
//Event Item Properties
public string EventTitle {get; set;}
public DateTime StartDate {get; set;}
public DateTime EndDate {get; set;}
public string EventContent {get; set;}
}
Then, whichever way you choose to construct the view model, you will map the view model properties to the domain model(s) in the controller. You can do this mapping manually
HtmlItemViewModel.ArticleTitle = ArticleItem.ArticleTitle;
//and so on...
Or you can use a third party tool like AutoMapper
I tend to favor the flattened view model in most scenarios as it allows me to only send the data I need, no more, no less. And it also allows me to put my data annotations for input validation on the view model instead of the domain model.
Assume the following models: (example taken from Breeze DocCode)
public class Customer {
public Guid CustomerID { get; internal set; }
public ICollection<Order> Orders { get; set; }
}
public class SomeDetail{
public string name{ get; set; }
}
public class Order {
public int OrderID {get; set;}
public Guid? CustomerID {get; set;}
public SomeDetail detail {get; set;}
}
Nested queries against single Navigation Properties are clear to me. How could this be done if the Navigation Property is a collection? Something like this:
var query = EntityQuery.from("Customers")
.where("Orders.detail.name", "==", someName);
As "Text":
Select all Customers where the name of the detail of any order this customer has equals someCondition?
I am running into errors here because
.where("Orders.detail.name, "=", someCondition)
is not possible due to the collection.
Is there a short way to check for this conditions without building up a number off collections and filtering per hand?
Any help much appreciated here.
As of Breeze 1.4.6, we have added support for two new query operators: "any" and "all"
This means that your query can now look something like this.
var query = EntityQuery.from("Customers")
.where("Orders", "any", "detail.name", "==", someName);
See: http://www.breezejs.com/documentation/query-examples
In my MVC Site a Teacher login and should view his classes and all books used in his classes.
The teacher should not be able to see someone else class or book.
Here's my model
public class Teacher
{
public int Id { get; set; }
public string Name {get; set;}
}
public class Books
{
public int Id {get; set;}
public string Title {get; set;}
public int CourseId {get; set;}
}
public class Course
{
public int Id {get; set;}
public int Name {get; set;}
public int TeacherId {get; set;}
}
I use a RepositoryPattern with UnitOfWork.
In order to get all classes tobe showned I should put in my Controller this kind of line :
var classes = classRepository.All.Where(x => x.TeacherId == currentTeacher.Id)
Things will get worst with the BookControler since I need to check all classes of the currentTeacher:
var classes = classRepository.All.Where(x => x.TeacherId == currentTeacher.Id)
var books = bookRepository.All.Where(x => classes.Contains(y => y.Id == x.CourseId)
It seems to me that this kind of approach may lead to some functional bug.
what I would like to do is that when a teacher login my repositories auto-filter in order to keep only datas concerning the currentTeacher, therefore in my controller I would have :
var classes = classRepository.All; // Already filtered on currentTeacher
or
var books = bookRepository.All; // Already filtered on currentTeacher
Is this possible? And how?
Create a new All(IPrincipal user) method in which you pass in the current authenticated user. This All method will lookup the Identity.Name value in your object context's Teacher collection to get the ID which is then returned.
i.e. .Where(x => x.TeacherId == currentTeacher.Id) is the return value from this new All method.