I use Linq to SQL as data access layer in ASP.NET MVC application. So the query result is a strong typed object. How can I dynamiclly specify which field to show in the page.
For example, the query result has the following fields:
FirstName
LastName
Address
Tel
My question is if one user wanna show the Lastname and the Firstname. 2nd user wanna show the Address and Firstname, 3rd etc... Different user has different requirements. So how can I query the database/filter the result based on user's specific requirement(on demand)?
To be more specific, the query result is a set of person information.
public class Person
{
public string FirstName {get;set;}
public string LastName {get;set;}
public string Tel {get;set;}
public string Tel {get;set;}
}
Take a look at System.Linq.Dynamic:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
1) User indicates in the UI what results the user wishes to see
2) The Controller interprets this and stores this for later
3) The Controller goes to the DAL and gets the data from the DAL
4) The Controller then modifies the return result somehow according to #2
5) The Controller then passes the modified data to the UI
6) The UI renders the data
I think your disconnect starts at 4 and may extend as far as 6.
The fact is that there are literally thousands of ways to do this. Here's one way to do it in amazingly C#-like pseudocode.
First, I'd create a view model that contains information on what I want to display to the user.
Original Linq to Sql (abbreviated):
public class Person
{
public string FirstName {get;set;}
public string LastName {get;set;}
public string Tel {get;set;}
}
My view model:
public partial class PeopleView
{
public bool ShowFirstName {get;set;}
public bool ShowLastName {get;set;}
public bool ShowTel {get;set;}
public IEnumerable<Person> People {get;set;}
}
The controller method that preps the model:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PersonDetails(bool showFirstName,
bool showLastName, bool showTel)
{
var viewData = new PeopleView()
{
ShowFirstName = showFirstname,
ShowLastName = showLastName,
ShowTel = showTel,
People = Dal.GetPeople()
};
return View(viewData);
}
And here's the View:
<% foreach(var item in ViewData.Model.People){ %>
<% if(ViewData.Model.ShowFirstName) {%>
<%= item.FirstName %><br/>
<% } %>
<% if(ViewData.Model.ShowLastName) {%>
<%= item.LasttName %><br/>
<% } %>
<% if(ViewData.Model.ShowTel) {%>
<%= item.Tel %><br/>
<% } %>
<% } %>
Use an if statement based upon user input. I am assuming you stored the User's preference somewhere, in which case the following code would do the trick:
if (showAddress)
{
var results = from u in Users
select new
{
FirstName = u.FirstName;
LastName = u.LastName;
Address= u.Address;
}
// Code to display results goes here
}
else
{
var results = from u in Users
select new
{
FirstName = u.FirstName;
LastName = u.LastName;
}
// Code to display results goes here
}
Related
I have a problem, I have the next controller
namespace RolesMVC3.Areas.Administrador.Controllers
{
[Authorize(Roles = "Adminr")]
public class HomeController : Controller
{
private BASEDATOSCJ_2Entities db = new BASEDATOSCJ_2Entities();
public ActionResult Index()
{
string username = User.Identity.Name;
MembershipUser user = Membership.GetUser(username);
Guid key = (Guid)Membership.GetUser().ProviderUserKey;
var Universities = (from u in db.UNIVERSITy
join s in db.CAMPUS_UNIVERSITy on u.IdUniversity equals s.IdUniversity
join c in db.CIUDAD_CAMPUS on s.IdCiudadSede equals c.IdCiudadSede
join co in db.OFFICE on s.Idoffice equals co.Idoffice
join uxc in db.USERxOFFICE on co.Idoffice equals uxc.Idoffice
where uxc.UserId == key
select new { u.Name, namecity = c.Nombre, s.Idoffice}).ToList();
return View(Universities);
}
With this controller I just want send to View u.Name, and s.Idoffice. How I do? (in fact do not know if this controllet is fine), I want to send fields belong to different tables. I want to send the query as a list and present at the View, ViewBag go with it?, How do I pass these data to the view and display with a foreach?.
I use razor
If you change the following line:
select new { u.Name, namecity = c.Nombre, s.Idoffice}
To
select new { Name = u.Name, Idoffice = s.Idoffice }
This only selects the two fields into a list. In your view you can do the following:
#model List<dynamic>
#foreach(dynamic d in Model) {
<p>#d.Name</p>
<p>#d.Idoffice</p>
}
Edit:
You might want to define a ViewModel to contain your data.
public class MyViewModel {
string Name {get;set;}
string Idoffice {get;set;}
}
Now you can change your select statement as follows:
select new MyViewModel { Name = u.Name, Idoffice = s.Idoffice }
And update your Razor file as such:
#model List<MyViewModel>
#foreach(MyViewModel d in Model) {
<p>#d.Name</p>
<p>#d.Idoffice</p>
}
I would use a view model. I have learnt not to expose my domain objects to the view, I rather map my domain object to the view model and return this view model to the view.
Separate you data access logic from your view logic. You can put that whole statement into a repository class and then you just call this method from the controller.
Here is a partial view model, you might have more properties if you need more data to be displayed:
public class UniversityViewModel
{
IEnumerable<University> Universities { get; set; }
}
University class:
public class University
{
public string Name { get; set; }
public string Idoffice { get; set; }
}
In my action method of my controller it would look something like this:
public ActionResult Index(int id)
{
UniversityViewModel viewModel = new UniversityViewModel
{
Universities = universityRepository.GetAll()
};
return View(viewModel);
}
And in my view I would have the following:
<table>
#foreach(University university in Model.Universities)
{
<tr>
<td>Name:</td>
<td>university.Name</td>
</tr>
}
</table>
This is just a basic display of data in the view, you can use 3rd party components to display your data with some features.
I'm building an internal page that allows trusted users to change a parameter setup manually through a form. The inputs to this setup are a list of setupparameters (of unknown size), each with a specific list of values. The user can then select a value for all or a subset of the parameters.
I have attempted to illustrate this with my current model for the view
public class SetupModel
{
public List<SetupParameter> Parameters { get; set; }
}
public class SetupParameter
{
public string ParameterName { get; set; }
// list with text=paramvalue, value=paramvalueid
public SelectList ParameterValueList { get; set; }
// id of the selected parametervalue if any
public int? SelectedParameterValueID { get; set; }
}
My current attempt at rendering a view for this:
<% using (Html.BeginForm("Update", "Parameters") {%>
...
<% foreach( var parameter in Model.Parameters ) { %>
<div><%: parameter.ParameterName %></div>
<div><%: Html.DropDownListFor(x => parameter.SelectedParameterValueID, parameter.ParameterValueList, "Please select") %></div>
<% } %>
...
My question is how can I render a view that allows me to submit the form and get a reasonably understandable model back to my form action that will allow me to obtain the list of selected parameter values. I'm not aware of the best practices or tricks here, so I will appreciate any feedback I get :)
You could try using a FormCollection:
public ActionResult Submit(FormCollection formCollection)
{
//Iterate form collection to get fields
return View();
}
You might find this post by Phil Haack useful: Model Binding To A List.
Also note that you'll need to post back an identifier (ParameterName, for example) for each parameter too, so you can indentify which value corresponds to a parameter back in the controller.
I’m using the Membership Provider and would like to display a list of all the users and their First Name, Last Name etc using the GetAllUsers function.
I'm having trouble understanding how to implement this function in MVC.
Has anyone implemented this in MVC or is there an easier way to list all the users in my application?
Any help or advise would be really helpful.
Controller
public ActionResult GetUsers()
{
var users = Membership.GetAllUsers();
return View(users);
}
View Model
public class GetUsers
{
[Required]
[DisplayName("User name")]
public string UserName { get; set; }
[Required]
[DisplayName("User name")]
public string FirstName { get; set; }
}
View
<%= Html.Encode(item.UserName) %>
Error
The model item passed into the dictionary is of type 'System.Web.Security.MembershipUserCollection', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Account.Models.GetUsers]'.
View
Inherits="System.Web.Mvc.ViewPage<MembershipUserCollection>"
<ul>
<%foreach (MembershipUser user in Model){ %>
<li><%=user.UserName %></li>
<% }%>
</ul>
Controller
public ActionResult Admin()
{
var users = Membership.GetAllUsers();
return View(users);
}
What's the difficulty you have with it? the GetAllUsers method simply returns a collection of users that you can then display ... either manually, or using a grid component from a vendor like Telerik.
something like:
<% foreach(var user in Membership.GetAllUsers()) { %>
<p>Name: <%= user.UserName %></p>
<% } %>
Obviously, heed the warning in the documentation:
Be careful when using the GetAllUsers
method with very large user databases,
as the resulting
MembershipUserCollection in your
ASP.NET page may degrade the
performance of your application.
There is an overload which lets you do paging to get around this :-)
#Jemes, the problem you're having is that you're passing a System.Web.Security.MembershipUserCollection as the model to your view and you specified that the model of your view was of type Account.Models.GetUsers. Change the type to System.Web.Security.MembershipUserCollection. However, if you're using the default Membership provider in your solution, you will not have the First Name available as the MembershipUser class doesn't have a FirstName property.
My strongly typed View inherits from a "Person" object that is created with Linq to SQL. In my "Edit" View, i have to display of course old values:
<%= Html.TextBox("FirstName") %>
"FirstName" is NCHAR, so it need to be trimmed. So i ended up with:
<%= Html.TextBox("FirstName", Model.FirstName.Trim()) %>
and this works. But when form is submitted (after POST) and some errors occur, i need to show it again:
[AcceptVerbsAttribute(HttpVerbs.Post), Authorize(Roles = "office"), HandleError]
public ActionResult Edit(Models.Person person)
{
if (!(_personService.ValidatePerson(person))) // Persona non valida
{ return View(person); }
}
If for some reason the user left the textbox "FirstName" blank, the resulting property Person.FirstName become null and Model.FirstName.Trim() throws an Exception (Object reference not set to an instance of an object).
Any way to modify the bind and have all string trimmed by default? Or any ideas to how fix this?
Update: seems confirmed to be a MVC 2 behaviour.. still looking for a good way to handle this. Actually using an extension method:
public static string TrimOrDefault(this string value)
{
return value != null ? value.Trim() : string.Empty;
}
Something is odd here.
Model.FirstName shouldn't be null; the model binder is smart enough to set an empty input field ( textbox ) to "". Make sure your property names match up between your model and the textboxes your using.
Which version of MVC are you using? 1 or 2? I'm running a MVC 1 version in VS 2008 and the only way I can get FirstName to be null is by not including it in the form at all.
I could see if you initial GET Edit view threw this error and you had FirstName set to nullable in your dbml and database but since it is a Post this doesn't make sense to me right now. ;)
Update:
I've confirmed this:
With an empty form:
VS 2008 - Mvc 1 - FirstName = ""
VS 2010 - Mvc 2 - FirstName = null
Uh oh... Thats going to break a lot of code...
The Code:
View ( same for both ):
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="FirstName">FirstName:</label>
<%= Html.TextBox("FirstName", Model.FirstName) %>
<%= Html.ValidationMessage("FirstName", "*") %>
</p>
<p>
<label for="LastName">LastName:</label>
<%= Html.TextBox("LastName", Model.LastName) %>
<%= Html.ValidationMessage("LastName", "*") %>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
VS 2010 - Mvc 2
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
public ActionResult AddPerson()
{
var person = new Person();
return View(person);
}
[HttpPost]
public ActionResult AddPerson(Person person)
{
return View(person);
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
VS 2008 - Mvc 1
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
public ActionResult AddPerson()
{
var person = new Person();
return View(person);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddPerson( Person person )
{
return View(person);
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
No clean fix atm. The model binder is actually setting those properties to null. Set First and Last to "" in the constructor and exclude those properties from binding: [Bind(Exclude="FirstName, LastName")] they stay "".
Is this documented someplace?
Try a custom modelbinder.
See this : ASP.NET MVC: Best way to trim strings after data entry. Should I create a custom model binder?
In Models.Person have the default value be String.Empty which should elminate the null error at least.
If you want to have it automated, then you can add that into your dbml file. There is a designer.cs file attached and each field has a getter and setter. you can add code in there if you wish.
you could also create a partial class based on your table and handle the trim within that. it means that if you make a change to the dbml file then you don't lose your changes.
if you're not using a dbml file then let us know.
EDIT
If you have a class called person in your dbml file then this will already be declared as partial.
create another class, in the same project and do something like the following;
public partial class Person
{
public string NewName
{
get { return this._name.Trim(); }
}
}
So from then on in you can use .NewName instead of name and it will come back trimmed. You can also add code in there to ensure it's not null, not red, not whatever and return the appropriate value.
I'm not aware of an extension method that does this, if someone else does then please let me know.
I create a simple HTML helper for fields that COULD be null.
public static string TrimOrDefault(string value)
{
return (value == null ? "" : value.Trim());
}
And then in your code you can use
<%= Html.TextBox("FirstName", Helpers.TrimOrDefault(Model.FirstName)) %>
It's re-usable for future nullable fields and reads easily.
Am using strongly typed view to show a complex object in a data entry/edit form. for eg: Model.UserInformation.Name, Model.LivingPlace.FacilitiesSelectList, Model.Education.DegreesList... etc. These information are shown in multiselect listbox, grids.. etc. User can change the information in the edit screen. Is there any way to post the Model object with user changes to controller on sumbit button click. Please suggest.
Regards,
SHAN
The same object instance that has been passed to the view: No. ASP.NET MVC uses a default Model binder to instantiate new action parameters from request values. So for example if you had the following action method:
public ActionMethod DoWork(User model)
{
return View();
}
public class Address
{
public string Street { get; set; }
}
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address[] Addresses { get; set; }
}
the binder will look in the request and try to bind model values. You could to the following in your View:
<%= Html.TextBox("FirstName") %>
<%= Html.TextBox("LastName") %>
<%= Html.TextBox("Addresses[0].Street") %>
<%= Html.TextBox("Addresses[1].Street") %>
This will automatically populate the values of your model in the controller action.
To avoid mass assignment of properties that shouldn't be bound from request values it is always a good idea to use the BindAttribute and set Exclude or Include properties.
Use <input type="text" name="UserInformation.Name"><input> to bind to subobjects.