I just started to learn MVC3. Have been coding in traditional ASP.NET For a while and now would like to move to MVC.
Some things that i don't understand (probably just been used differently then in traditional ASP.NET)
I'm trying to write a simple news module that will display news and allow to insert comment for them.
So first step is, i created a tables on my SQL server:
TblNews
TblCategories
TblComments
Created Linq2SQL data Class in Models folder and named it News.dbml
Created Controller HomeController.cs and a method called Index() in it.
Look like this:
public ActionResult Index()
{
Models.NewsDataContext db = new Models.NewsDataContext();
var Model = (from n in db.TblNews
select new
{
ID = n.ID,
Title = n.Title,
Description = n.Description,
Category = n.TblCategory.CategoryName
});
return View(Model);
}
As you see I'm trying to select all the news and their category names (TblNews and TblCategories do have relationship between them)
After that I'm returning the data that i got from the query.
In the View i have:
#{
ViewBag.Title = "News Index Page";
}
<table>
#foreach (var item in Model)
{
<tr>
<td>
ID: #item.ID<br />
Title: #item.Title<br />
Description: #item.Description<br />
Category: #item.Category
</td>
</tr>
}
</table>
Which should return something like:
ID: 4
Title: asd
Description: asd
Category: 2
That's my sample data from tables.
When i run the page it gives me error message:
'object' does not contain a definition for 'ID'
but when i focus my mouse on "item" variable it actually contain:
{ ID = 4, Title = asd, Description = asd, Category = Test2 }
I also tried to return Linq Query as list by adding .ToList() to the end of it.
Can anybody give me any hints and get me into the right direction?
Sorry if i explained something incorrectly. English is not my primary language.
Please ask if you need any more information.
Thank you very much.
P.S. I'm using Visual Studio 2012
You're missing your model declaration at the top of your view so will default to type object. When you do item.ID you're doing object.ID, which is why it complains that object does not contain a definition for ID
Add the following:
#model List<Models.NewsDataContext>
Also, make sure you evaluate the LINQ query by calling .ToList() before your return Model, that way the query is already executed against the database before it hits the view:
return View(Model.ToList());
Edit: Actually, in your LINQ query you are selecting into an anonymous type, you must use a concrete class in order to use this in your view. The below assumes your TblNews corresponds to a class called News:
var Model = (from n in db.TblNews
select new News //class name here
{
ID = n.ID,
Title = n.Title,
Description = n.Description,
Category = n.TblCategory.CategoryName
});
return View(Model.ToList());
Then change your model to:
#model List<News>
Related
I am trying to add records from table position for positionName(s) to let user select a position for employee when editing.My last attempts is to add a navigation property like field in company model
public virtual ICollection<Position> Mpositions { get; set; }
But all I got so far is null ref exception or no element in viewModel with property "PositionName" ass per viewbag didn't bother using everybody keeps recommending to avoid it so not going to do so either.
public ActionResult Edit([Bind(Include = "CompanyID,CompanyName,EntityForm,Address,Dissolute,CreationDate,FiscaleYear,Description")] Company company)
{
var GlbUpdate = db.Companies.Include(c => c.Members).Include(p => p.Mpositions);
List<Company> mdlCompanies = new List<Company>();
foreach (var item in GlbUpdate)
{
if ((item.Mpositions==null) || (item.Mpositions.Count() == 0))
{
item.Mpositions = (ICollection<Position>)new SelectList(db.Positions.Except((IQueryable<Position>)db.Positions.Select(xk => xk.Members)), "PositionID", "PositionName");
}
mdlCompanies.Add(item);
//I tried first to edit the Mpositions property directly in gblUpdate
//item.Mpositions = (IEnumerable<Position>)db.Positions.Select(p => new SelectListItem { Value = p.PositionID.ToString(), Text = p.PositionName}) ;
//(ICollection<Position>)db.Positions.ToListAsync();
}
In the view I have this
List<SelectListItem> mPositionNames = new List<SelectListItem>();
#*#this yields no results if I try gettign it from the compani record itself it gives a logic error where all id match all positionNames impossible to select an item and only positions already registered are available on the dropDownlist*#
#{foreach (var item in Model.Mpositions)
{
mPositionNames.Add(new SelectListItem() { Text = item.PositionName, Value = item.PositionID.ToString(), Selected = (false) ? true : false });
#*#selected attribute set to false not an issue, no data to select from :p so far*#
}
}
#*#null exception(if i try to midify Mpositions directly in controler) here or empty list if modify it then put it with original query in a new list*#
<div class="SectionContainer R-sectionContainerData" id="MwrapperDataRight">
#Html.DropDownListFor(mpos => item.PositionID, (SelectList)Model.Mpositions)
</div>
All I want to do is pull the positions table to create a drop downList so users can change the position of an employee but since position has a 1>many relation with employee not companies it is not bound automatically by EF nor I seem to be able to use Include() to add it.
Your query for editing positions are complex. This query must edit person's info only. Using Edit action for formalizing position's edit are not correct.It's againts to Single Responsibility Principle. Use ViewComponents for this situation. Load positions separately from person info.
I found a suitable solution using a model that encapsulate the other entities then using Partialviews/RenderAction so each part handles one entity/operation.
Say I have a table that begins like this:
#foreach (var item in Model)
{
<tr>
<td>
#Html.ActionLink(item.Name, "Things", new { someId = item.Id})
</td>
And it takes me to the view for that specific item. Once I am in that separate .cshtml file, how can I reference back to the original "item.Name" string that appeared in this ActionLink?
* SOLUTION EDIT *
The solution ended up looking like this.
The ActionLink:
#Html.ActionLink(item.Name, "Things", new { someId = item.Id, someName = item.Name})
The Action:
public ActionResult Things(Guid? someId, string someName)
...
ViewBag.currentName = someName;
The other View:
<h2>#ViewBag.currentName</h2>
You can add an extra parameter with the name of the item
#Html.ActionLink(item.Name, "Things", new { someId = item.Id, name = item.Name})
You will need to modify your controller and model too for this approach.
I think you don't have to reference to first ActionLink argument. What might interest you is an object which is a Model for this view. When you click the link you will navigate to action where you pass the someId - id of current model object. Then probably you should find an object by its id in your storage. It will have .Name property that was used as ActionLink argument.
However if you just need exactly the same string you've used as a first argument in ActionLink which can be any string you should duplicate it in route values. Your Things action should accept not only someId argument but also this string. After you should pass this string via Model or ViewBag/ViewDictionary to your view.
I am refactoring somebody else's app written badly in ASP.NET MVC 4 (Razor). [.NET is not my specialty - I do primarily C/C++]. He is not doing database access the MVC canonical way. He is using a Repository class for each table to execute all of his SQL commands on SQL Server rather than the let the basic Entity Framework do the heavy lifting. The app has to:
read a list of rows from a table from the controller (he has a Model for the table).
return this list of model objects to a view (ViewBag or ViewData would be easiest).
insert the list members in a table or list in the View.
select a single row (representing a from the list return the selected row to a controller ActionResult- a single table record in the model class
In the repository class (partial code) called by the controller:
public List<Note> GetNotesByWorkGroup(string workGroup)
{
List<Note> notes = new List<Note>;
string sql = "SELECT * FROM tblNotes where WorkGroup = 'Foo';
SqlCommand sqlCmd = new SqlCommand(sql, conn);
SqlDataReader myReader = sqlCmd.ExecuteReader();
if (myReader.HasRows)
{
while (myReader.Read())
{
Note note = new Note();
note.NoteId = myReader.GetInt32(NoteIdOrdinal);
note.DateTime = myReader.GetDateTime(DateTimeOrdinal);
note.WrittenBy = myReader.GetString(WrittenByOrdinal);
notes.Add(note);
}
}
return notes;
}
The controller receives this and must pass it to a view. His way of passing the list is:
List<Note> notes = noteRepository.GetNotesByWorkGroup();
ViewData["myList"] = notes;
In the view, he fouled everything up. Simplest solution would be to either create a table or list with an onclick(note.Id) for each element which must be clickable (a button or href?). Each row will need a clickable element and one other text element from the Note object for a two-parm row display.
For example, it would need to look like this, with Date the selectable field and WrittenBy just text.
Date1 WrittenBy
Date2 WrittenBy
Date3 WrittenBy
Then a javascript function can receive the Id onclick() and submit it to the controller ActionResult().
I just need a sample of View code (MVC 4, C#, Razor) showing how the ViewData["myList"] gets dereferenced from C# to HTML and inserted into the rows in the list or table. Also, how would I write a submit in a javascript function that passes a parm?
Any suggestions and examples for the View?
you can perform this task using ViewModel,ViewBag or ViewData approach.
ViewModel
foreach (var item in Model.myList)
{ }
ViewBag or ViewData
You can create a table and then can loop through all data.
<table>
<tr>
<td>date</td>
<td>writtenby</td>
</tr>
#foreach (var item in ViewData["myList"])
{
<tr>
<td>#item.date</td>
<td>#item.writtenby</td>
</tr>
}
</table>
My first suggestion would be return notes from controller instead of setting the View Data
If not you not you shoudl be able to access ViewData["myList"] like an array. ViewData["myList"][0].properyName.
I hope that helps if I can find some sample code, I will update this post.
#{
List<Note> _list = ViewData["myList"] as List<Note>;
}
#if (_list != null)
{
foreach (var item in _list)
{
#Html.ActionLink(item, "Index");
}
}
Try it Hope it will help.Thanks
I have got two different tables. User and ProjectDetails. There are two different controllers as well to do CRUD operations on these tables. Now, I have a case where, in the User CREATE operation, I have to select the Project from the List of Projects in ProjectDetails. I tried the following:
In the user model, I created this line:
public IEnumerable<ProjectDetail> ProjectDetail { get; set; }
And in the controller, in the create Action, I have added the following code:
public ActionResult Create()
{
var model = new UserDetail
{
ProjectDetail = db1.ProjectDetails
};
return View(model);
}
And in the create view, I am trying to get the list of Project IDs as follows:
#Html.DropDownListFor( x => x.ProjectDetail, new SelectList(Model.ProjectDetail, "Project ID"))
However, wen i run, i get the number of lines (as equal to the number of projects) as
System.Data.Entity.DynamicProxies.ProjectDetails_F########### (Some numbers)..
Please can someone help me?
Regards,
Hari
[EDIT] - I checked in the debug mode and found the following.. Tried attaching the image..
I drilled down that Proxy things and found ProjectID there. How can I get that?
You are using a wrong overload, use this instead:
#Html.DropDownListFor( x => x.ProjectDetail,
new SelectList(Model.ProjectDetail, "ProjectId","ProjectName"))
// where ProjectId is the unique identifier field of `ProjectDetail`
// and `ProjectName` is the text you want to show in the dropdown
In your code you are not telling the html helper what properties to use for the datavalue and the datatext. The overload you use is the one where you tell the htmlhelper which value is selected.
You can do something like
var projection = db1.ProjectDetails.Select(t => new ProjectDetailsViewModel
{
Prop1 = t.Prop1,
Prop2 = t.Prop2
});
Can you try
public ActionResult Create()
{
var model = new UserDetail
{
ProjectDetail = db1.ProjectDetails.ToList()
};
return View(model);
}
I'm trying to set up a page where I display a list of items and the details of the selected item. I have it working but wonder whether I have followed the correct approach. I'll use customers as an example
I have set the aspx page to inherit from an IEnumerable of Customers. This seems to be the standard approach to display the list of items. For the Details I have added a Customer user control which inherits from customer.
I think i'm on the right track so far but I was a bit confused as to where I should store the id of the customer whose details I intend to display. I wanted to make the id optional in the controller action so that the page could be hit using "/customers" or "customers/1" so I made the arg optional and stored the id in the ViewData like this:
public ActionResult Customers(string id = "0")
{
Models.DBContext db = new Models.DBContext();
var cList = db.Customers.OrderByDescending(c => c.CustomerNumber);
if (id == "0")
{
ViewData["CustomerNumber"] = cList.First().CustomerNumber.ToString();
}
else
{
ViewData["CustomerNumber"] = id;
}
return View("Customers", cList);
}
I then rendered the User control using RenderPartial in the front end:
<%var CustomerList = from x in Model
where x.CustomerNumber == Convert.ToInt32(ViewData["CustomerNumber"])
select x;
Customer c = (Customer)CustomerList.First(); %>
<% Html.RenderPartial("Customer",c); %>
Then I just have an actionLink on each listed item:
<%: Html.ActionLink("Select", "Customers", new { id = item.CustomerNumber })%>
It all seems to work but as MVC is new to me I would just be interested in others thoughts on whether this is a good approach?
In regards to proper MVC and separations of concerns, you shouldn't be calling LINQ queries in your view. To get around that, change your controller action code from what you have to this:
if (id == "0")
{
ViewData["CustomerDetails"] = cList.First();
}
else
{
ViewData["CustomerDetails"] = From x in db.customers where x.id = cInt(id);
}
then your partial
<% Html.RenderPartial("Customer",ViewData["CustomerDetails"]); %>
Are you showing the customer information on the same screen that you have your list of customers and not a separate view?
In this case I would take the following approach.
Display a list of customer's, be it a listbox or drop down list.
Let's assume it's a drop down list, probably not very user friendly
You would have the text as the customer name and then the value as the customer id
<select title="Customers">
<option label="Pieter" value="001"></option>
</select>
and using JQuery's $.getJSON you could load the new data via a call to an asp.net MVC action.
Note that your Action should return JsonResult