How to Use a View Model with LINQ to Entities - asp.net-mvc

I'm new to ASP.NET MVC, but I cant seem to understand the rules concerning View Models. The example I'm showing here definitely resembles other examples from various sources so I'm totally flumuxed.
I've used Code First With Entity Framework 6 on VS 2013 MVC 5, and created the following two Models.
public class Task
{
public int TaskId { get; set; }
public int UserId { get; set; }
public String Title { get; set; }
public String Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; }
public String UserName { get; set; }
public String Password { get; set; }
public String FullName { get; set; }
public String Email { get; set; }
public String Status { get; set; }
public String Role { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
And here is my Context.
public class FRESHContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Task> Tasks { get; set; }
public DbSet<Artefact> Artefacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public System.Data.Entity.DbSet<FRESH.ViewModel.ManageTasksViewModel> ManageTasksViewModels { get; set; }
}
Now seeing that there is a one to many relationship between User and Task, I want to list tasks out showing which User has been assigned to them (Well this 'is' just a learning example), so I figured that this would benefit from designing a special View Model to strongly type to the View page and to use a LINQ to Entities query to derive the model to send to the View Page. Here is my View Model to be used with the strongly typed View Page.
public class ManageTasksViewModel
{
public int TaskId { get; set; }
public int UserId { get; set; }
public String FullName { get; set; }
public String Title { get; set; }
public String Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
And don't worry, I've correctly strongly typed the View Page as follows;
#model IEnumerable<FRESH.ViewModel.ManageTasksViewModel>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.UserId)
</th>
<th>
#Html.DisplayNameFor(model => model.FullName)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
etc etc .............(with brevity in mind lol.)
So now I create a LINQ to Entities query, which seems quite reasonable to me after watching, oh I don't know a thousand videos, lol.
public class ManageTasksController : Controller
{
private FRESHContext db = new FRESHContext();
// GET: ManageTasks
public ActionResult Index()
{
var query = (from U in db.Users
join T in db.Tasks on U.UserId equals T.UserId
select new ManageTasksViewModel
{
TaskId = T.TaskId,
UserId = U.UserId,
FullName = U.FullName,
Title = T.Title,
Description = T.Title,
StartDate = T.StartDate,
EndDate = T.EndDate
}).Take(10).ToList();
return View("Index",query);
}
And of course it built! yeehaar!, but when the code gets to the ActionResult Index(), I get this Exception.
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: The entity or complex type 'FRESH.DAL.ManageTasksViewModel' cannot be constructed in a LINQ to Entities query.
So what have I done wrong, and also could I do this better another way, I haven't looked into LINQ to SQL, as I thought that this method would surely work. Any help would be much appreciated. Thank you.

You should not have a viewmodel in your dbcontext. This is a viewModel only and is totally different than your entities.
public System.Data.Entity.DbSet<FRESH.ViewModel.ManageTasksViewModel>
ManageTasksViewModels { get; set; }

Thanks for all those who gave their time to help me on this, Basically I was going over the Contoso University tutorial again and got the answer to this, basically it's ridiculously similar to my first attempt, except that I was trying.
select new ManageTasksViewModel
{
.
.
.
};
whereas I should have been saying;
select new ManageTasksViewModel()
{
.
.
.
}
public class ManageTasksController : Controller
{
private FRESHContext db = new FRESHContext();
// GET: ManageTasks
public ActionResult Index()
{
IQueryable<ManageTasksViewModel> query = from tasks in db.Tasks
join users in db.Users on tasks.UserId equals users.UserId
select new ManageTasksViewModel()
{
TaskId = tasks.TaskId,
UserId = users.UserId,
FullName = users.FullName,
Title = tasks.Title,
Description = tasks.Description,
StartDate = tasks.StartDate,
EndDate = tasks.EndDate
};
return View(query.ToList());
}
Anyway hope this is useful to someone.

Related

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.

Entity Framework SQL not optimal on navigation property

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);

MVC: best way to display data from 2 tables in 1 view

I'm trying to make a view that has data from 2 tables in my database. I have looked up a couple things and I tried to use a ViewModel but I can't get it to work. Then I was thinking about using a view and a partial view to display data from the 2 tables. What is the best way using the ViewModel or using a view and a partial view?
Also if someone knows what I am doing wrong with my ViewModel and wants to help that would be awesome.
my 2 models:
public partial class prospect
{
public prospect()
{
this.result = new HashSet<result>();
}
public int id { get; set; }
public int testid { get; set; }
public string name { get; set; }
public string surname { get; set; }
public string email { get; set; }
public string cellnumber { get; set; }
public System.DateTime timestampstart { get; set; }
public Nullable<System.DateTime> timestampend { get; set; }
public Nullable<int> totalresult { get; set; }
public string birthdate { get; set; }
public string school { get; set; }
public Nullable<int> grade { get; set; }
public string address { get; set; }
public string cityandcode { get; set; }
public virtual ICollection<result> result { get; set; }
public virtual resultpersection resultpersection { get; set; }
}
public partial class resultpersection
{
public int id { get; set; }
public int prospectid { get; set; }
public int sectionid { get; set; }
public Nullable<int> result { get; set; }
public virtual prospect prospect { get; set; }
public virtual section section { get; set; }
}
The things I want to display are:
prospect.name , prospect.surname, prospect.totalresult and all the results per section of this prospect(this comes from the resultpersection table)
my viewmodel:
namespace testAptitude.Models
{
public class ResultsPerProspect
{
public List<prospect> prospect { get; set; }
public List<resultpersection> resultPerSection { get; set; }
}
}
my controller
public ActionResult Index()
{
ResultsPerProspect vm = new ResultsPerProspect();
vm.prospect = (from p in db.prospect select p).ToList();
vm.resultPerSection = (from rps in db.resultpersection select rps).ToList(); ;
List<ResultsPerProspect> viewModelList = new List<ResultsPerProspect>();
viewModelList.Add(vm);
return View(viewModelList.AsEnumerable());
}
my view
#model IEnumerable<testAptitude.Models.ResultsPerProspect>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table>
<thead>
<tr>
<th class="col-sm-3">
#Html.DisplayNameFor(model => model.prospect)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(model => item.prospect)
</td>
</tr>
}
</tbody>
</table>
I can't do item.prospect.name because it say's that is doesn't contain a definition for name
and what is dispalys now is this:
prospect
System.Collections.Generic.HashSet`1[testAptitude.Models.result]
Thanks in advance!
With your current code you can access it using
#Html.DisplayNameFor(model => model.FirstOrDefault().prospect)
You are passing IEnumerable<testAptitude.Models.ResultsPerProspect> to your view i.e multiple objects of class ResultsPerProspect. You would need to iterate throught this List. Each items in this list will contain definition for prospect and resultPerSection.
Or you can pass single object of class ResultsPerProspect as you are just adding single element in list.
UPDATE
You have List Of ResultsPerProspect. and each item of ResultsPerProspect has List of prospect and List of resultPerSection. So you would need to first iterate for loop over List Of ResultsPerProspect. and inside that for loop , for loop for List of prospect List of and resultPerSection
CODE
#foreach (var item in Model)
{
foreach (var pros in item.prospect)
{
<tr>
<td>
#Html.DisplayFor(model => pros.Name)
</td>
</tr>
}
}
Why don't you create a class ("Container") that consists of your two (sub)classes (let's say A and B)? You can then create a Container object and put the needed objects in Container.A and Container.B. You can then easly pass "Container" to your view and access your objects.

How to get Country Name based on its Id in Entity framework using Mvc?You can just have a look at the following things

The above is my database .I have for tables Contact,Countries,States,Cities.The Country in contact table is foreign key to the Id in countries table. And I am using entity framework to get the data.I am using database first approach.I am getting my Contact details by the following code
public class ContactManager
{
public static List<Contact> GetContacts()
{
using (var obj = new EntityDBConnectionString())
{
return obj.Contacts.ToList();
}
}
}
And in the Controller part I have like this
public ActionResult ViewContacts()
{
return View("ViewContacts",ContactManager.GetContacts());
}
and atlast the View(cshtml)
#model List<Entity.Data.Contact>
#{
ViewBag.Title = "ViewContacts";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ViewContacts</h2>
<table style="font-family:'Segoe UI';font-size:20px;display:table" border="1" cell-spacing="1" >
#foreach (var item in Model)
{
<tr style="border:1px solid black;padding-right:3px;background-color:lightblue;display:table-row">
<td>#item.Id</td>
<td>#item.Name</td>
<td>#item.Mobile</td>
<td>#item.EmailId</td>
<td>#item.Address</td>
<td>#item.Country</td>
</tr>
}
</table>
My problem is that I am getting the CountryId's in the output table as it is a foreign.Can I get the Country Name as output
The output I am getting is the below one
How to get the Country name in the data part and pass to the controller and how to pass it to the view? Can anyone help me out .Thanks in advance!!!!!!
The below is my Contact class
public partial class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailId { get; set; }
public string Mobile { get; set; }
public string Address { get; set; }
public Nullable<int> City { get; set; }
public Nullable<int> State { get; set; }
public Nullable<int> Country { get; set; }
public virtual City City1 { get; set; }
public virtual Country Country1 { get; set; }
public virtual State State1 { get; set; }
}
and the edmx file is
I asume that you have set database realtionship for country and contact table. now in view you can get country name using
<td>#item.Country.Name</td>
Please Include your Country table in EF query like to bring the country
public static List<Contact> GetContacts()
{
using (var obj = new EntityDBConnectionString())
{
return obj.Contacts.Include("Country").ToList();
}
}
and use the below in your view
#item.Country.Name

MVC Multiple tables one model

I am having difficulty with my understanding of MVC coming from an aspx world.
I have a Model called CustomerGarment. This has a Order and a Customer along with a few garments.
public class CustomerGarment
{
public int CustomerGarmentId { get; set; }
public virtual Customer Customer { get; set; }
public virtual Order Order { get; set; }
public virtual GarmentJacket GarmentJacket { get; set; }
public virtual GarmentShirt GarmentShirt { get; set; }
}
I have a method for get and post. When the page loads, it creates a new CustomerGarment instance and querys the database to fill the Customer and Order variables. I then use the viewbag to show on the screen a list of GarmentJackets and GarmentShirts
The page then views and using the view I can access the model perfectly. Drop downs load with the viewbag contents and I can access all Customer and Order variables using the model I have passed.
The problem I then face is when I use the HttpPost. The model is not passed back with the information I passed to it.
public ActionResult AddGarments(int orderId, int customerId)
{
CustomerGarment cg = new CustomerGarment();
cg.Order = (from a in db.Orders where a.OrderId == orderId select a).FirstOrDefault();
cg.Customer = (from a in db.Customers where a.CustomerId == customerId select a).FirstOrDefault();
var jackets = from a in db.GarmentJackets orderby a.Type, a.SleeveLengthInches, a.ChestSizeInches select a;
var shirts= from a in db.GarmentKilts orderby a.PrimarySize, a.DropLength select a;
ViewBag.GarmentJacket = new SelectList(jackets, "GarmentJacketId", "GarmentJacketId");
ViewBag.GarmentShirt = new SelectList(shirts, "GarmentShirtId", "GarmentShirtId");
return View(cg);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddGarments(CustomerGarment cg)
{
// Here, I do not have the customer info for example
db.CustomerGarments.Add(cg);
db.SaveChanges();
return RedirectToAction("Index");
return View(cg);
}
This is a bit of my view
#Html.HiddenFor(model => model.Order.OrderId)
#Html.HiddenFor(model => model.Order.CustomerId)
<div class="display-field">
#Html.DisplayFor(model => model.Customer.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.GarmentJacket, "Jacket")
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.GarmentJacket, (SelectList)ViewBag.GarmentJacket, new {style="width:312px;height:30px;margin-top:2px;margin-bottom:5px"})
</div>
EDIT
My Garment Jacket Model
public class GarmentJacket : Garment
{
public int GarmentJacketId { get; set; }
[Required]
public string Type { get; set; }
[Required]
[Display(Name = "Chest Size")]
public int ChestSizeInches { get; set; }
[Required]
[Display(Name = "Sleeve Length")]
public int SleeveLengthInches { get; set; }
}
public class Garment
{
[DataType(DataType.Date)]
public DateTime? DateRetired { get; set; }
[Required]
public string Barcode { get; set; }
[Required]
public bool Adults { get; set; }
}
In your CustomerGarment class, you should have:
public class CustomerGarment
{
public int CustomerGarmentId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
public int GarmentJacketId { get; set; }
public int GarmentShirtId { get; set; }
public virtual Customer Customer { get; set; }
public virtual Order Order { get; set; }
public virtual GarmentJacket GarmentJacket { get; set; }
public virtual GarmentShirt GarmentShirt { get; set; }
}
And, then, in your View, your DropDownList will look like:
#Html.DropDownListFor(m => m.GarmentJacketId, (SelectList)ViewBag.GarmentJacket, new {style="width:312px;height:30px;margin-top:2px;margin-bottom:5px"})
Your DropDownList only posts one value, which is the GarmentJacketId. You can't bind that Id to the whole GarmentJacket class.
By the way, you also need to replace your hidden inputs with these:
#Html.HiddenFor(model => model.OrderId)
#Html.HiddenFor(model => model.CustomerId)
I think I know your problem. As you suggested in you comment above you need to post everything you want retained in the view. This is one of the differences beteween webforms and MVC, webforms has viewstate that could contain information that you don't explicitly add to the view and post back, giving the impression of state. In MVC you have to add it to the view.
On the other hand you don't need to pass in more information than you need either. You pass inn the customerId as a hidden field. On post method you get the customer from the db using the Id, then you add the order to the customer.
I have some questions about your design, but given that a customer holds a collection of Orders, you could do something like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddGarments(CustomerGarment cg)
{
// Get the customer from the database
var customer = db.Customers.Find(c=>c.id==cb.Customer.Id)
var order = new Order();
//Create your order here using information from CustomerGarment model
//If the model already holds a valid Order object then just add it.
//i.e. you could get a Garment object from the DB using the GarmentId from
//the ViewModel if you really need more than just the Id to create the order
customer.Orders.Add(order);
db.SaveChanges();
return RedirectToAction("Index");
}

Resources