Entity Framework SQL not optimal on navigation property - asp.net-mvc

I have simple code first model (generated from db) with 3 entities:
[Table("Note")]
public partial class Note
{
public Note()
{
NoteCompanies = new HashSet<NoteCompany>();
}
public long ID { get; set; }
[Column(TypeName = "text")]
public string Content { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<NoteCompany> NoteCompanies { get; set; }
}
[Table("Company")]
public partial class Company
{
public long ID { get; set; }
[StringLength(150)]
public string Name { get; set; }
}
[Table("NoteCompany")]
public partial class NoteCompany
{
public long ID { get; set; }
public long NoteID { get; set; }
public long CompanyID { get; set; }
public virtual Company Company { get; set; }
}
When i use this Model inside ASP MVC View like:
#model Models.Note
<ul>
#for (var company in Model.NoteCompanies.Select( nc => nc.Company ))
{
#company.Name
}
</ul>
Entity framework fires single select query for each Company. I would expect that Entity would use produce JOIN query like:
SELECT {fields}
FROM
NoteCompany NC
INNER JOIN Company C ON NC.CompanyId = C.Id
WHERE
NC.NoteId = #Param
Is it possible to force EF to produce JOIN query instead of single row SELECT?
Best Regards
IT Man

It will be better to write something like this:
#{
var ids = Model.NoteCompanies.Select(nc => nc.CompanyID).ToList();
for (var company in db.Companies.Where(x => ids.Contains(x.ID)).ToList())
{
#company.Name
}
}
Or try to get NoteCompanies with corresponding Companies at controller via eager loading:
model.NoteCompanies = db.NoteCompanies.Include(x => x.Company).ToList();
return View(model);

Related

How to make a ViewModel for an anonymous type from a linq query

I have joined and grouped 2 databases using linq in my controller, and now I need to parse it to the view. I know, I gonna need a ViewModel to do it, but I have no clue what to put in it, to make it work.
My Linq query in the controller looks like this:
var energyUsageViewModel =
from e in applicationDbContext
join u in _context.UsagesPerMonth on e.Id equals u.EnergyUsageId into u2
group u2 by new { Year = e.Year }
into u3
select u3.ToList();
return View(energyUsageViewModel);
In short what the linq query does is taking the "year" from table "e" joining it with table "u" which contains the energy usage per month, which I am gonna use to make a table in my view, which displays the year and usage per month in one row.
And currently my ViewModel looks like this (obviously not working):
public class EnergyUsageViewModel
{
public IEnumerable<UsagePerMonth> UsagePerMonthVm { get; set; }
}
The view takes a model of:
#model IEnumerable<Jullerup.Models.EnergyUsageViewModel>
I have tried to modify the ViewModel to take u3, but haven't succeeded.
I get the following invalid operation exception:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Linq.Enumerable+SelectEnumerableIterator2[System.Linq.IGrouping2[<>f__AnonymousType101[System.String],System.Collections.Generic.IEnumerable1[Jullerup.Models.UsagePerMonth]],System.Collections.Generic.List1[System.Collections.Generic.IEnumerable1[Jullerup.Models.UsagePerMonth]]]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable`1[Jullerup.Models.EnergyUsageViewModel]'.
How do I edit my ViewModel to handle u3?
Update:
I'm working with 3 classes.
Customer class:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
//Navigation Prop
public ICollection<EnergyUsage>? energyUsage { get; set; }
}
EnergyUsage class:
public class EnergyUsage
{
public int Id { get; set; }
public int CustomerID { get; set; }
public DateTime Date { get; set; }
public int YearlyHeatUsage { get; private set; }
public List<UsagePerMonth> UsagePerYear { get; set; }
//Navigation Prop
public Customer? Customer { get; set; }
}
UsagePerMonth class:
public class UsagePerMonth
{
public int Id { get; set; }
public MonthEnum Month { get; set; }
public int Usage { get; set; }
public int HeatUsage { get; private set; }
public string EnergyType { get; set; }
private EnergyMeasurement energyType { get; set; }
public int EnergyUsageId { get; set; }
}
In the database Customer.Id (PK) has a one to many relationship to EnergyUsage.CustomerId (FK) and EnergyUsage.Id (PK) has a one to many relationship to UsagePerMonth.EnergyUsageId (FK).
What I am trying to do, is to get an object/list something I can use in the view to display all instances of UsagePerMonth grouped by Customer.Year for a certain Customer.Id.

repeater foreach in asp.net mvc

I'm building a website in ASP.Net, using MVC, and need to list a set of results
but i get error in the code
model:
public class Customers
{
public int Id { get; set; }
public string Name { get; set; }
public List<Customers> Itemlst { get; set; }
}
controller:
public ActionResult List()
{
Customers itemobj = new Customers();
return View(itemobj);
}
view:
#foreach(var item in Model.Itemlst)
{
<tr>
<td>Items ID:</td>
<td>#item.ID</td>
<td>Items Name:</td>
<td>#item.Name</td>
</tr>
}
</table>
From the NullReferenceException that you are receiving we can see that the issue is because of the Itemlst not being initialised. One of the ways to solve this is just to make sure that there is a valid list when you create the object:
public class Customers
{
public Customers()
{
Itemlst = new List<Customers>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Customers> Itemlst { get; set; }
}
So you can add values to the list in your action if need:
public ActionResult List()
{
Customers itemobj = new Customers();
var example = new Customers ();
example.Id = 1;
example.Name = "Example";
itemobj.Add();
return View(itemobj);
}
I don't know if you are just using this as an example for your question, but I can't help but notice that there is something weird. You could use something different like:
public class ViewModel // Name to what makes sense to you
{
// Some other properties...
public List<Customer> Customers { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
Or you could just use List<Customer> as your model in the view directly (yes, your model can be a object which is simply a list of objects).
When you pass the Customers list to the view, this list itself is the model.
Change Model.Itemlst —> Model inside the foreach loop.
This will iterate the list of customers.

Select multiple table with Linq to Sql

I have two tables. There is one-to-many relationship between these tables.I want to select Company table and BankAccount List table (for appropriate CompanyID).
How can I do it with Linq-to-Sql?
public class Company
{
// My Fields
[Key]
public Guid ID { get; set; }
public string FullName { get; set; }
// My virtual properties FOR relationships(one-to-one,one-to-many etc.).
public virtual List<BankAccount> BankAccounts { get; set; }
}
and
public class BankAccount
{
// My Fields
//[ScaffoldColumn(false)]
[Key]
public Guid ID { get; set; }
[ForeignKey("Companies")]
public Nullable<Guid> CompanyID { get; set; }
public string BankName { get; set; }
// My virtual properties FOR relationships(one-to-one,one-to-many etc.).
public virtual Company Company { get; set; }
}
I write this as follow, but I didn't like it
List<List<BankAccount>> bankaccounts = new List<List<BankAccount>>();
foreach (var comp in companyRepository.Companies)
{
List<BankAccount> banks = new List<BankAccount>();
foreach (var bank in bankRepository.BankAccounts)
{
if (comp.ID == bank.CompanyID)
{
banks.Add(bank);
}
}
bankaccounts.Add(banks);
banks = new List<BankAccount>();
}
I think the following will yield the same result.
var bankaccounts = companyRepository.Companies.Select(c => c.BankAccounts)
.ToList();
If you are using entity framework, you can eagerload the 'BankAccounts' property to minimize DB calls.
Hope this helps.

Generate list in view and pass it to controller

I'm writing a hotel reservation web site with Asp.net and MVC 4.
it have a class named reservation which have a list of contacts. in create view i want to dynamically create contacts. (number of contacts = adults + kids and it will be determined in create reservation view)
how could i post contact information to controller?
public partial class Reservation
{
public int Id { get; set; }
public int RoomType_Id { get; set; }
public System.DateTime FromDate { get; set; }
public System.DateTime ToDate { get; set; }
public byte Adults { get; set; }
public byte Kids { get; set; }
public decimal Price { get; set; }
public int User_Id { get; set; }
public int State_Id { get; set; }
public virtual ReservationState ReservationState { get; set; }
public virtual RoomType RoomType { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
should i set a maximum number for contacts(for example 5 and then write something like this?
[HttpPost]
public ActionResult Create(Reservation reservation,Contact Adult1,Contact Adult2, Contact Adult3, Contact Adult4, Contact Adult5, Contact Kid1,Contact Kid2, Contact Kid3)
{
if(reservation.Adults>0)
reservation.Contacts.Add(Adult1);
if(reservation.Adults>1)
reservation.Contacts.Add(Adult2);
...
if (ModelState.IsValid)
{
_db.Reservations.Add(reservation);
_db.SaveChanges();
return RedirectToAction("Index");
}
}
it's very dirty is there a better way? can i pass list of contacts?
#for (var i = 0; i < Model.Contacts.Count(); i++)
{
#Html.EditorFor(m => m.Contacts[i])
}
The only thing you need to do is instantiate a list of new Contacts. This is why a view model is preferable as you could simply do this in the constructor based upon some value on your view model:
public class ReservationViewModel
{
public ReservationViewModel()
{
Contacts = new List<Contact>();
for (var i = 0; i < Adults + Kids; i++)
{
Contacts.Add(new Contact());
}
}
...
}
Alternatively, after you see the code that gets generated you'll understand how the modelbinder expects to receive the data back. Your inputs will look like:
<input id="Contacts_0__Name" name="Contacts[0].Name" />
Where 0 is the iteration count of contacts. If you simulate this structure manually, the modelbinder will pick up the data just as well.

LINQ to entities against EF in many to many relationship

I'm using ASP.NET MVC4 EF CodeFirst.
Need help to write LINQ (to entities) code in Index action to get collection of Courses which are attended by selected student. The relationship is many to many with join table with payload.
//StudentController
//-----------------------
public ActionResult Index(int? id)
{
var viewModel = new StudentIndexViewModel();
viewModel.Students = db.Students;
if (id != null)
{
ViewBag.StudentId = id.Value;
// *************PROBLEM IN LINE DOWN. HOW TO MAKE COURSES COLLECTION?
viewModel.Courses = db.Courses
.Include(i => i.StudentsToCourses.Where(t => t.ObjStudent.FkStudentId == id.Value));
}
return View(viewModel);
}
The error I got is:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
I have modeles (the third one is for join table with payload):
//MODEL CLASSES
//-------------
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentToCourse> StudentsToCourses { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public virtual ICollection<StudentToCourse> StudentsToCourses { get; set; }
}
public class StudentToCourse
{
public int StudentToCourseId { get; set; }
public int FkStudentId { get; set; }
public int FkCourseId { get; set; }
public string Classroom { get; set; }
public virtual Student ObjStudent { get; set; }
public virtual Course ObjCourse { get; set; }
}
Then, here is modelview I need to pass to view
//VIEWMODEL CLASS
//---------------
public class StudentIndexViewModel
{
public IEnumerable<Student> Students { get; set; }
public IEnumerable<Course> Courses { get; set; }
public IEnumerable<StudentToCourse> StudentsToCourses { get; set; }
}
EF does not support conditional include's. You'll need to include all or nothing (ie no Whereinside the Include)
If you need to get the data for just certain relations, you can select it into an anonymous type, something like (the obviously untested);
var intermediary = (from course in db.Courses
from stc in course.StudentsToCourses
where stc.ObjStudent.FkStudentId == id.Value
select new {item, stc}).AsEnumerable();
Obviously, this will require some code changes, since it's no longer a straight forward Course with a StudentsToCourses collection.

Resources