I have a Table ECmain that has a relationship with 2 other tables, Notes and Email.
I want to access the data in the view so that a list of toes will appear for every record in ECmain and be editable.
Here is my Model
namespace EditSuite.Models
{
public class ECmain
{
public ECmain()
{
this.Notes = new Collection<Notes>();
}
public int ID { get; set; }
public string Auth { get; set; }
public string KeyWords { get; set; }
public string Description { get; set; }
public string URL { get; set; }
public string Category { get; set; }
public string SubCategory { get; set; }
public string Title { get; set; }
public string Live { get; set; }
public virtual ICollection<Notes> Notes { get; set; }
public virtual ICollection<Email> Email { get; set; }
public List<Notes> NoteList { get; set; }
}
public class Email
{
public int ID { get; set; }
public int ECmainID { get; set; }
public string EmailText { get; set; }
public virtual ECmain ECmain { get; set; }
}
public class Notes
{
public int ID { get; set; }
public int ECmainID { get; set; }
public string NotesText { get; set; }
public virtual ECmain Ecmain { get; set; }
}
DB Context
namespace EditSuite.DAL
{
public class ECContext : DbContext
{
public ECContext()
: base("name=ECmodelConnection")
{
}
public virtual DbSet<ECmain> ECmain { get; set; }
public virtual DbSet<Email> Email { get; set; }
public virtual DbSet<Notes> Notes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
Controller
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using EditSuite.DAL;
using EditSuite.Models;
namespace EditSuite.Controllers
{
public class ECmainsController : Controller
{
private ECContext db = new ECContext();
// GET: ECmain
public ActionResult Index()
{
//var church = from m in db.ECmain.Take(10)
// where m.Live == "Y" && m.Auth == "Yes"
// select m;
//church = church.OrderBy(m => m.Category);
return View(db.ECmain.Take(10));
}
View
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Notes)
</td>
<td>
#Html.DisplayFor(modelItem => item.KeyWords)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.URL)
</td>
<td>
#Html.DisplayFor(modelItem => item.Category)
</td>
<td>
#Html.DisplayFor(modelItem => item.SubCategory)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Live)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
The result is
Server Error in '/' Application.
Invalid column name 'ECmain_ID1'.
Invalid column name 'ECmain_ID1'.
Invalid column name 'ECmain_ID'.
Invalid column name 'ECmain_ID1'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'ECmain_ID1'.
Invalid column name 'ECmain_ID1'.
Invalid column name 'ECmain_ID'.
Invalid column name 'ECmain_ID1'
I tried
#Html.DisplayFor(modelItem => item.NoteList)
But that returned nothing
This sounds a lot like this problem:
ASP.NET MVC /Entity Framework Error - Invalid column name 'Environment_Id'
The answer there may be helpful to you.
Gerry
3rd Party Edit
For me the answer linked by Gerry could be of use to your problem. I have not worked much with EF and MVC but based on your Exception System.Data.SqlClient.SqlException: Invalid column name 'ECmain_ID1' or 'ECmain_ID' i assume
that the sql statement that is created by entity-framework does not match the columns in your database
or that the matching between table-columns from your sql-result and the properties of you email-object fails
Related
I have two Objects, let says Employee and Address as follows
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int DepartmentId {get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
The relationship model is that each employee belongs to a single department (using DepartmentID property)
I want to create a view which shows all employees and instead of displaying the department ID, the department name is shown instead
My controller looks like this
public class EmployeeController : Controller
{
private readonly IAppRepository _appRepository;
public EmployeeController(IAppRepository appRepository)
{
_appRepository = appRepository;
}
public ViewResult List
{
Department department;
// retrieve all employee from DBContext (SQL Server)
var model = _appRepository.GetEmployees;
// retrieve department details
department = _appRepository.GetDepartment(model.DepartmentId);
ViewBag.department = department;
}
}
My problem seems to be with the last two lines (//retrieve department details. as model is returning an enumerable of employee.
How can this be achieved, I was thinking using ViewModel but have not found a way yet. thanks
In my opinion, you could configure relationships between Employee and Department,refer to here
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int DepartmentId { get; set; }
[ForeignKey("DepartmentId")]
public Department Department { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
In your repository GetEmployees, you could get all employees with their own Department using Include, refer to here
var employees = _context.Employees.Include(e => e.Department).ToList();
Then your model of var model = _appRepository.GetEmployees; will contains Department info for each employee and you could achieve name in view,for example
#model IEnumerable<Employee>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Department)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Department.Name)
</td>
</tr>
}
</tbody>
</table>
HTML.BeginCollectionItem does not return values to the controller. It always return NULL in the controller. I am not sure if has got anything to do if there is a partial view within another partial view. Below is the snippet of the code/view.
ProductEditModel
public class ProductEditModel
{
// Product details displayed on edit form
public Product ProductModel { get; set; }
public IList<ProductAssetAudioEditModel> ProductAssetAudios { get; set;}
}
ProductAssetAudioEditModel
public class ProductAssetAudioEditModel
{
public int ProductId { get; set; }
public int? ProductAssetId { get; set; }
public virtual IList<ProductAssetResourceEditModel> ProductAssetResources { get; set; }
}
ProductAssetResourceEditModel
public class ProductAssetResourceEditModel
{
public int? ProductAssetResourceId { get; set; }
public int ProductAssetId { get; set; }
public int ResourceNumber { get; set; }
public int? ElectronicFileId { get; set; }
public ElectronicFile ElectronicFile { get; set; }
}
ProductEditView.cshtml
<div id="audio">
#foreach (ProductAssetAudioEditModel audio in Model.ProductAssetAudios)
{
Html.RenderPartial("_ProductAssetAudioRow", audio);
}
</div>
_ProductAssetAudioRow.cshtml
#using (Html.BeginCollectionItem("ProductAssetAudios"))
{
....
<tbody>
#foreach (var resource in Model.ProductAssetResources)
{
Html.RenderPartial("_ProductAssetAudioResource", resource);
}
</tbody>
.....
}
_ProductAssetAudioResource
#using (Html.BeginCollectionItem("ProductAssetResources"))
{
#Html.HiddenFor(m => Model.ProductAssetResourceId)
#Html.HiddenFor(m => Model.ProductAssetId)
<td>
#if (Model.ElectronicFileId.HasValue)
{
#Html.HiddenFor(model => model.ElectronicFileId)
#Html.ActionLink(Model.ElectronicFile.FileName, "Details", "File", new { id = Model.ElectronicFileId, area = "Edi" }, null);
}
</td>
<td>
#Html.EditorFor(c => Model.TrackTitle)
</td>
}
In the controller , ProductAssetResources is NULL even though edit page binds the properties correctly for editing.
I am not sure what I am missing here.
-Alan-
I have an Expenses and Categories tables. I have seeded the tables with few entries, but when pulled from DB the Category property is null. I would expect it to be automatically populated with entries from Category table. What am I missing?
Model:
public class Expense
{
public int ExpenseId { get; set; }
[DataType(DataType.Currency)]
public decimal Amount { get; set; }
[ForeignKey("CategoryId")]
public Category Category { get; set; }
[Required(ErrorMessage = "You must select some category!")]
public int CategoryId { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Description { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
}
View:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Amount)
</td>
<td>
#item.CategoryId
</td>
<td>
#item.Category.Description //NullReferenceException here
</td>
</tr>
Controller:
// GET: Expenses
public ActionResult Index()
{
return View(db.Expenses.ToList());
}
Try marking the Category property as virtual. Navigation properties need to be virtual to support the lazy loading if it is not eager loaded.
Noob warning - learning MVC for an academic project :-)
I have two models and I'm trying to show ProjectName (from first model) in my index view of Actors (from my second model). Everything loads up OK and I'm able to display projectId from my Actor class, but it's not picking up a value for #Html.DisplayFor(modelItem => item.project.ProjectName) in my index table.
However, it is picking up the correct [DisplayName("Project Name") in the table, which makes me think it is making some connection - just can't understand why it's not getting the value for it, I just have an empty column with a correctly labelled header!
I had planned on using a ViewModel to achieve this, but in the Wrox Professional ASP.NET MVC 4 book, the authors do what appears to be the same as what I am attempting in showing the Genre & Artist names (not ID's) in their Album view (MVC Music Store project - http://www.asp.net/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-4).
Help much appreciated!
public class Project
{
public int ID {get; set;}
[DisplayName ("Project Name")]
public string ProjectName { get; set; }
[DisplayName("Client")]
public string ClientID { get; set; }
public string Description { get; set; }
[DisplayName("Use Cases")]
public virtual ICollection <UseCase> UseCases { get; set; }
}
...
public class Actor
{
public int ID { get; set; }
public int projectID { get; set; }
public Project project { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
Here is my controller code for the Index action (note, I'm passing the projectId in the URL and this is what I'm using as the parameter...
// GET: Actors
public ActionResult Index(int? id)
{
ViewBag.projectId = id;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
else
{
return View(db.Actors.Where(x => x.projectID == id).ToList());
}
}
And here is the view code....
#model IEnumerable<JustSpecIt.Models.Actor>
#{
ViewBag.Title = "Actors";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Actors</h2>
<p>
#Html.ActionLink("Create New", "Create", new { id = ViewBag.projectId })
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.project.ProjectName)
</th>
<th>
#Html.DisplayNameFor(model => model.projectID)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.project.ProjectName)
</td>
<td>
#Html.DisplayFor(modelItem => item.projectID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
#Html.ActionLink("<< Back", "ShowSteps", "Projects", new { id = ViewBag.projectId }, null)
Try to add virtual keyword to project property in Actor class to allow lazy loading of the related Project
public class Actor
{
public int ID { get; set; }
public int projectID { get; set; }
public virtual Project project { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
See here for more info regarding lazy loading related entities: http://msdn.microsoft.com/en-us/data/jj574232#lazy
public class Project
{
public int ID {get; set;}
[DisplayName ("Project Name")]
public string ProjectName { get; set; }
[DisplayName("Client")]
public string ClientID { get; set; }
public string Description { get; set; }
[DisplayName("Use Cases")]
public virtual ICollection <UseCase> UseCases { get; set; }
}
public class Actor
{
public int ID { get; set; }
public int projectID { get; set; }
public virtual Project project { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
// GET: Actors
public ActionResult Index(int? id)
{
ViewBag.projectId = id;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
using (var context = new DbContext()) // initiate a connection to the DB
{
var actors = context.Actors.Where(x => x.projectID == id);
// tell it to include the projects
actors.Include("project"); // the name of the property
// now retrieve list from the DB
actors.ToList();
// return the actors to the view
return View(actors);
}
}
So the DbContext is what ever you context is called. We wrap it in a using to let make sure we manage the connection properly.
The virtual keyword in front of Project project {get;set;} is to enable Lazy loading, which can be bad in some cases (Causes SELECT N + 1 problems). To make sure we include all the projects in our list of actors we use the Include function and pass the name of the property through.
To bypass issues with SELECT N + 1, we call the ToList() method before we pass the values to the view. If we don't then the view will execute the get actors SQL and for each actor it will execute a call to get the project. So if we have 10 actors, we will make 11 calls. Which isn't good. ToList() makes sure that we execute 1 call, that provides us with all the values we will need in the view.
Then we execute the DB call with ToList(). And pass the values back to the view.
I'm trying to generate a table in a razor view using reflection to pull the properties from the model.
Here is what I've tried:
#if (#Model.Count() > 0)
{
System.Reflection.PropertyInfo[] properties = Model.First().GetType().GetProperties();
<table>
<thead>
<tr>
#foreach (var property in properties)
{
if (char.IsLower(property.Name.ToCharArray()[0])) //ignore foreign keys
{
continue;
}
<th>#property.Name</th>
}
</tr>
</thead>
<tbody>
#foreach (PCNWeb.Models.Switch item in Model)
{
/*System.Reflection.PropertyInfo[]*/ properties = item.GetType().GetProperties();
<tr>
#foreach (var property in properties)
{
<td>
#Html.DisplayFor(modelItem => item.[property.Name])
</td>
}
</tr>
}
</tbody>
</table>
}
Let me point out the part of the code that I'm not sure what to do with:
<td>
#Html.DisplayFor(modelItem => item.[property.Name])
</td>
The property.Name contains the name of the property of item that I want to access.
If I were to hand write the innermost td one example would be:
<td>
#Html.DisplayFor(modelItem => item.Switch_Location)
</td>
where "Switch_Location" is the value of property.Name
So basically I need to access the value of a property of item based on the name of the property stored in a variable.
EDIT adding model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace PCNWeb.Models
{
public partial class Switch
{
public Switch()
{
this.Ports = new List<Port>();
this.Switch_Location = new Switch_Location();
this.Switch_Model = new Switch_Model();
this.UPS = new UPS();
}
[Key]
public int switchRecId { get; set; }
[Required]
public int locationRecId { get; set; }
[Required]
public int modelRecId { get; set; }
//public int gatewayRecId { get; set; }
[Required]
public int upsRecId { get; set; }
[Required]
public int Number { get; set; }
[Required]
[StringLength(64)]
public string Name { get; set; }
[StringLength(80)]
public string Description { get; set; }
[StringLength(32)]
public string Cabinet { get; set; }
[StringLength(40)]
public string Power_Feed { get; set; }
[Required]
public Nullable<int> ipOctet1 { get; set; }
[Required]
public Nullable<int> ipOctet2 { get; set; }
[Required]
public Nullable<int> ipOctet3 { get; set; }
[Required]
public Nullable<int> ipOctet4 { get; set; }
public virtual ICollection<Port> Ports { get; set; }
public virtual Switch_Location Switch_Location { get; set; }
public virtual Switch_Model Switch_Model { get; set; }
public virtual UPS UPS { get; set; }
}
}
So basically I need to access the value of a property of item based on the name of the property stored in a variable.
No, you need to access the value of a property based on a PropertyInfo object describing it. That's far far easier.
property.GetValue(item)
If you dont really need the DisplayFor method, you can do it like this in your loop:
<tbody>
#foreach (PCNWeb.Models.Switch item in Model)
{
/*System.Reflection.PropertyInfo[]*/ properties = item.GetType().GetProperties();
<tr>
#foreach (var property in properties)
{
<td>
#property.GetValue(item,null)
</td>
}
</tr>
}
</tbody>