I'm using MVC (for the first time) with Entity framework, Database first
What I want to do is display data from a database in a single view. I created the database first, then I made a ADO.NET Entity Data Model based from the database that contains all the tables. I then created a Index view that was strongly typed with my Entity Data Model as model.
In my Index I have at the top
#model IEnumerable<Forum6.Models.Forum>
This allows me to get the rows from the table "Forum" from my database. If I try to add an extra model I get I get this error message when I run:
Line 1: #model IEnumerable<Forum6.Models.Forum>
Line 2: #model2 IEnumerable<Forum6.Models.Post>
Parser Error Message: Only one 'model' statement is allowed in a file.
After searching for an answer I found this: Two models in one view in ASP MVC 3
The answer was to create a ViewModel (ParentModel) that contained all the Models (Tables).
This is the ViewModel I created:
public class ViewModel
{
public IEnumerable<Forum6.Models.Forum> Forum { get; set; }
public IEnumerable<Forum6.Models.Post> Post { get; set; }
public IEnumerable<Forum6.Models.Topics> Topics { get; set; }
public IEnumerable<Forum6.Models.Users> Users { get; set; }
public IEnumerable<Forum6.Models.PrivMsg> PrivMsg { get; set; }
public IEnumerable<Forum6.Models.Permission> Permission { get; set; }
}
I edited my controller to look like this:
public class HomeController : Controller
{
// ForumDBEntities old_db = new ForumDBEntities();
ViewModel db = new ViewModel();
public ActionResult Index()
{
return View(db);
}
}
Then replaced the old Index view with a new strongly typed view that used the ViewModel as model. Which contains:
#model IEnumerable<Forum6.Models.ViewModel>
Trying to run this gives me this error:
The model item passed into the dictionary is of type 'Forum6.Models.ViewModel', but this dictionary requires a model item
of type
'System.Collections.Generic.IEnumerable`1[Forum6.Models.ViewModel]
How do I make the "ViewModel" enumarable? Or is my error elsewhere?
You'll need to change #model IEnumerable<Forum6.Models.ViewModel> to #model Forum6.Models.ViewModel as you're wrapping your IEnumerables inside a single ViewModel.
A good rule of thumb is to have a 1:1 relationship between your ViewModel and View.
This might be a good read for you: http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/ (just ignore the automapper part if you don't want to go that route)
You'll also need to put in actual data in your ViewModel since
ViewModel db = new ViewModel();
public ActionResult Index()
{
return View(db);
}
will just give your view an empty ViewModel.
One way to do it would be.
public ActionResult Index()
{
var model = new ViewModel
{
Forum = db.GetForum(),
Post = db.GetPost(),
Topic = you get the idea
};
return View(model);
}
One last thing when naming properties or variables in general you should use the plural verb when it contains a list. So your ViewModel would be.
public class ViewModel
{
public IEnumerable<Forum6.Models.Forum> Forums { get; set; }
public IEnumerable<Forum6.Models.Post> Posts { get; set; }
public IEnumerable<Forum6.Models.Topics> Topics { get; set; }
public IEnumerable<Forum6.Models.Users> Users { get; set; }
public IEnumerable<Forum6.Models.PrivMsg> PrivMsgs { get; set; }
public IEnumerable<Forum6.Models.Permission> Permissions { get; set; }
}
Change #model IEnumerable<Forum6.Models.ViewModel> to #model Forum6.Models.ViewModel as you are passing a single instance of a ViewModel class and not a collection of them.
All your collections are passed in a single instance of a view model.
Related
I've got a couple questions regarding DBContext and achieveing my desired Results.
I can retrieve data like this
---Model
public class InvoiceModel
{
[Key]
public int? Invoice_Number { get; set; }
public decimal Amt_Total { get; set; }
public decimal Amt_Due { get; set; }
public decimal Amt_Paid { get; set; }
public List<InvoiceModel> GetInvoice()
{
using (VuittonEntities db = new VuittonEntities())
{
return (from inv in db.Invoice
select new InvoiceModel
{
Invoice_Number = inv.Invoice_,
Amt_Total = inv.AmountTotal,
Amt_Due = inv.AmountDue,
Amt_Paid = inv.AmountPaid
}).ToList();
}
}
--- Controller
public ViewResult Index()
{
var data = new InvoiceModel().GetInvoice();
return View(data);
}
This is pretty much standard LinQ and it returns my Invoice table with the 4 fields I selected. But now I want to achieve this using DB Context. So I added this new Class to my controller and called it in the view like this.
Controller
public class VuittonEntities : DbContext
{
public DbSet<InvoiceModel> Invoice { get; set; }
}
View
public ViewResult Index()
{
VuittonEntities db = new VuittonEntities();
return View(db.Invoice.ToList());
}
It returns the Entire table and I have to comment out my List'InvoiceModel' class
Assumptions:
VuittonEntities is my Connection String Name,
Invoice is the table
Vuitton is my .edmx class --
I heard that this can be achieved without having to use a Select Statement. It appears I am not linking my model class to a LinQ entity but when I add my context class, it throws errors on my inv."FieldName" columns in my List{InvoiceModel} Class.
TLDR Version:
How Can I return selected fields from DbContext Class instead of entire table
Why does adding a DBContext class raise errors on my List(ModelClass) fields
If you don't want the entire table, then don't just call ToList() on the DbSet, as that will just grab all of the table as you have seen.. You had it right when using Select, since the Select statement is meant to create a projection from one set of data into a new form (new class, model, anonymous object).
I am learning Entity Framework and MVC.
This is my model:
public class ChatLogContext : DbContext
{
public ChatLogContext()
: base("connString")
{
}
public DbSet<ChatLogs> ChatLogs { get; set; }
}
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
}
And this is my controller code here:
public ActionResult Index()
{
using(var db = new ChatLogContext())
{
var list = db.ChatLogs.Select(p => p.Message).SingleOrDefault();
ViewBag.data = list;
return View();
}
}
I then access that data in view like this:
#model Chat.Models.ChatLogs
#Html.Raw(ViewBag.data)
I can access 1 record as seen here with this.
But I would like to learn, how to access all records from the table ChatLogs with Entity Framework and pass it to view with Razor method(foreach), so I can format that data (I don't like default tables that VS generates). I am now using ViewBag for one row and 1 column, this is the most far I came.
I just can't find an examples on Google that would help my brains.
Help appreciated.
PS: Is it better to work with pure entity or mix linq(linq to entities)?
Typically the Index action is for showing a grid of all the entities (in this case ChatLogs).
One of the points of the Razor View Engine is that you get typed Views. So typically I would pass the data to the view directly as opposed to using the ViewBag.
public ActionResult Index()
{
using(var db = new ChatLogContext())
{
var list = db.ChatLogs.ToList();
return View(list);
}
}
The next step is to have the View typed to IEnumerable<ChatLog>. Visual Studio should help you with that. Then you can just foreach over the ChatLogs.
I have a master view model, with the following two lists that are from two different DB tables,
public IEnumerable <Customer> SomeCustomer { get; set; }
public IEnumerable <CustomerSite> CustomerSites { get; set; }
In my controller I have
public ViewResult Index()
{
masterViewModel sitesModel = new masterViewModel();
return View(sitesModel);
}
Then in my view I write
#model IEnumerable<trsDatabase.Models.masterViewModel>
If I try to access the model by using a for each statement
#foreach (var customer in Model)
When I type #customer. It will only let me access the two lists not the individual properties in the list.
I thought I would have been able to do this with e.g.
#customer.CustomerSites.UnitNo
But the furthest i can go is
#customer.CustomerSites
And this obviously will not let me access the individual properties in the lists
Does anyone have any ideas on what I'm doing wrong here? Do I need to define the properties from the viewmodel in the controller? (I think not)
Update
I have the following code in my viewmodel,
namespace trsDatabase.Models
{
public class masterViewModel
{
public IEnumerable <Customer> Customers { get; set; }
public IEnumerable <CustomerSite> CustomerSites { get; set; }
}
}
Then the following in the controller,
public ViewResult Index()
{
masterViewModel sitesModel = new masterViewModel();
return View(sitesModel);
}
Then I changed my view declaration to
#model trsDatabase.Models.masterViewModel
This allows me to access the properties from the view perfectly, but it throws an error with the foreach loop as it can't be used when the view is inheriting the model rather than the Ienumerable list, do I need to change the syntax for rendering the list in the view and use something other than a foreachloop?
I think you need another loop, as CustomerSites is a collection:
#foreach (var customer in Model)
foreach (var site in customer.CustomerSites)
#site.UnitNo
EDIT AFTER COMMENT
Create a ViewModel class:
class ViewModel
{
public IEnumerable<Customer> Customer { get; set; }
}
Populate it in the controller and pass it to View() function.
Change the view to be typed (#model ViewModel).
Then inside view your Model property will be of type ViewModel and you can access customers collection through #Model.Customers
I think I am getting somewhere with mvc.
I have created a view that is bound to my view model that looks like this
public class CreatePurchaseViewModel
{
public Order Order { get; set; }
public IEnumerable<Product> Products { get; set; }
public DateTime OrderDate { get; set; }
public bool OrderSent { get; set; }
}
I have also created another view model
public class ProductsViewModel
{
public int ProductID { get; set; }
public int OrderID { get; set; }
}
I have create a partial view that is bound to this viewmodel.
I am loading it from my view
#Html.Partial("_AddProductItem", new MVC_WireFrame.ViewModels.ProductsViewModel())
Now how do I get the items from my ProductsViewModel into my CreatePurchaseViewModel or is that not the correct way of doing things?
I can access the data from the ProductsViewModel in my controller like
[HttpPost]
public ActionResult InsertProduct(string input)
{}
2 questions really, how do I pass the value into the other viewcontroller? and
how do I populate a list within the AddProduct partial view so I can show what products have been added?
Thanks in advance for any help.
Normally you should not be passing data from one view model to another. You should create your view model classes in such a way that they contain all the required information for the view to display. It is the controller's responsibility to fill this information. Concerning you second question about the list, well you simply add a property which is of type IEnumerable<T> to this view model which will be filled by the controller and the view can show what products have been added. So think of what information your views require and add it to the view models as properties.
In my MVC application I have a problem with passing data from view to controller. I have fairly complex domain classes:
public class TaskBase : PersistableObject
{
public virtual TaskCategory Category { get; set; }
public virtual IList<TaskNote> Notes { get; set; }
public virtual string TaskTitle { get; set; }
public virtual string TaskBody { get; set; }
public virtual DateTime? CreationTime { get; set; }
public virtual User CreatedBy { get; set; }
public virtual int CompletionRatio { get; set; }
}
public class MainTask : TaskBase
{
public virtual IList<TaskBase> ChildTasks { get; set; }
public virtual User AssignedTo { get; set; }
public virtual IList<TaskHistory> History { get; set; }
}
public class TaskFormModel : ViewDomainBase
{
public MainTask Task { get; set; }
public LoginForm LoginInfo { get; set; }
}
And in my view I want to pass an instance of TaskFormModel to the controller.
<%= Html.ActionLink<TaskController>("Edit Task", (x) => x.Edit(new TaskFormModel() { Task = item, LoginInfo = Model.LoginInfo }))%>
And here is the controller action:
public ActionResult Edit (TaskFormModel taskInfo)
{
return View(ViewPageName.TaskDetailsForm, task.Task);
}
In this action method taskInfo comes null even if I pass non-null instance from view. I think I have a binding problem here. I think, writing custom model binder requires every property to be converted and also when new fields added then binder class should also be changed, so I don't want custom model binder to do this. Is there any other way to pass data to controller in this scenario? Or could custom model binder can be coded so that less code written and also when new properies are added binder class will not need to be changed?
Edit After Comments: What I am trying to achieve is basically to pass an instance from one view to another view, without querying repository/db in my controller's action.
First version of answer:
Your GET edit method should be like:
public ActionResult Edit (int id)
{
var model = taskRepository.GetTaskEditModel(id);
return View(ViewPageName.TaskDetailsForm, model);
}
and ActionLink:
<%= Html.ActionLink("Edit Task", "Edit", "Task", new { model.Task.id })%>
If you want to pass complex objects to controller, you should wrap them up in html form and pass to POST action.
In my opinion you are doing something wrong.
As I understand: you are trying to instantiate a new object, pass it to browser and get it back.
well you cant.
If object you want to edit exists already in your storage, then you should alter your ActionLink to reference it by id, and instantiate it inside your Edit action.
Take a look at default strongly typed index views created by tooling.