I'm assuming this is a very very simple question. I'm wondering if I'm making a huge mistake because I supposed that this would be obvious but It's not working.
I'm trying to show a view which is using a viewmodel created specifically. Let's assume we are talking about:
public Order ActualOrder {get;set;}
public OtherType OtherVariable {get;set;}
public OtherType OtherVariable {get;set;}
Order is a complex type which holds some fields like:
[Required]
public int OrderId {get;set;}
[Required]
public string ClientName {get;set;}
[Required]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DateAndTime {get;set}
I'm mapping in the view as:
#Html.EditorFor(p => p.Order.OrderId)
#Html.EditorFor(p => p.Order.ClientName)
#Html.EditorFor(p => p.Order.DateTime)
Everything works perfect in this line of communication. However, when I try to read the viewModel at the Action the variables are not set and, for example, the DateTimes are set to 01/01/0001 (which is wrong) but the client name is correctly set.
Viewmodel and Order have default constructor. Also, If I see the HTML generated the 'name' attribute seems to be correctly set.
I can't see the interactions that can be harming the mapping.
IMPORTANT
I've noticed that the problem comes from a jQuery DataPicker component. However, I'm using Firebug and I don't see 'name' or 'id' changing. Why the framework is not able to bind the model?
Related
I'm trying to migrate a Html.ListBoxFor() over to a Html.Telerik().MultiSelectFor() to gain a bit of fancy UI, but the HTTP form posts are incompatible with the Model, and it seems excessive to make a new model binder to avoid it.
public class Settings
{
public int[] UserIds { get; set; }
public IEnumerable<SelectListItem> UserSelectList { get; set; }
}
When I use the Html.ListBoxFor helper, the HTTP form post includes just the value of the Select List Item (i.e. an array of UserIds), as expected.
UserIds[0] = 1
UserIds[1] = 2
#Html.ListBoxFor(model => model.UserIds, Model.UserSelectList)
When I use the Kendo MultiSelect, the HTTP form post includes two properties. Which is not expected.
UserIds[0].Text = Adrian
UserIds[0].Value = 1
...
#Kendo().MultiSelectFor(model => Model.UserIds).BindTo(Model.UserSelectList)
Does anyone know a way to get the Kendo MultiSelect to just post the id value back, or is there a ready-made Model Binder?
You just need to set the Primitive value to true which will get only the value list.
.ValuePrimitive(true)
For reference:
Is Primitive Sample
I am new to ASP.NET MVC 3.0 and trying to build an application using the MVC ViewModel design..
I was wondering what the best practices are regrading controllers for ViewModels and have a few questions below.. This is my understanding so far (which might be wrong)..
We create Models.
Create ViewModels by making a new class and declairing attributes with the same name and type as the base model classes (including ID fields of the base models you want to update later.. and the classes are not linked in any way).
Create a Repository for each of the base models (to find and save data ect.).
Create a Controller action for each of the ViewModels (which access the repositories of the base classes to retrieve values and put these values into the ViewModel, then pass the ViewModel to the ViewModels View).
Create Views from the ViewModels (ViewModel Views)
In the ViewModel controller Update (POST) method recieve the updated ViewModel object and convert it into base model objects (Maybe use Automapper?) and next save the base model objects back using their repositories and apply binding like this? "TryUpdate<'IPerson>(person)", "TryUpdate<'IPlace>(place);" (this looks wrong, the aim is to put the values back into the base classes from the ViewModel, apply binding, save base models back using the repositories!.. This doesnt appear to use the repositories.. Instead of TryUpdate<'IPerson>(person); I would expect to see something like this: person.Save(IPerson).. where "person contains the values", ".Save is the repository", and "IPerson contains the binding attributes to use for binding"?.. Not sure if this is right..
So far I have created ViewModels by making a new class and adding attributes from different base models using the same names. At this point i have the following questions:
Q1: Does each ViewModel have its own controller and access each of the base models repository classes to get its values?
Q2: in the ViewModel should you include the ID field of all of the base models that you are using attributes from, considering that you might want to POST an Update back through the ViewModels Controller to the base Models repository (needing the ID values)?
Q3: How would you bind attributes using an interface for binding the model in the controller using the repository to save.
I have been unable to find a tutorial or resource that explains everything in a step by step example, A complete answer would be the following example:
2x simple models, 1x simple viewModel, 1x interface for binding, 1x simple controller using an interface class for binding on update, 1x repository.. i.e.
//Model1
public class Person
{
int PersonID {get;set;}
string FirstName {get;set;}
string LastName {get;set;}
DateTime DOB {get;set}
}
//Model2
public class Place
{
int PlaceID {get;set;}
string Description {get;set;}
string AreaType {get;set;}
string PostCode {get;set;}
}
//ViewModel (containing attributes from models)
publc class ViewModel
{
//Person attributes
int PersonID {get;set;}
string FirstName {get;set;}
string LastName {get;set;}
//Place attributes
int PlaceID {get;set;}
string Description {get;set;}
string AreaType {get;set;}
//other attributes
string someOtherAttributeForDisplay {get;set}
}
//Model1 interface (for binding on model)
public interface IPerson
{
string FirstName {get;set;}
}
//Model2 interface (for binding on model)
public interface IPlace
{
string Description {get;set;}
string AreaType {get;set}
}
//ViewModelController?
{
//What goes here?
}
//Repository?
{
//what goes here?
}
I think you may have overcomplicated a very simple concept.
First off some general rules:
Don't use TryUpdateModel. Just don't.
For the same reasons, don't use any "auto" mapping mappers to map from your view model to your entities. Auto mapping the other way round (from entity to view model) is fine.
Your use of interfaces is unnecessary.
View models are supposed to be very simple classes that contain just the information you need for your view. If your view POSTs different information from what it displays, then just create a different view model for POST.
We use a naming convention of {Controller}{Action}Model for our view models. So for an action named "List" on a "Post" controller we will have a model called "PostListModel".
Finally, check out my response here Real example of TryUpdateModel, ASP .NET MVC 3
It seems weird that I couldn't find an explanation of the difference between those two helpers, so I would assume that is something obvious but I missed.
Basically I am trying to decide which one I should use for my case, with the following simple Model:
public class Booking
{
public int ID { get; set; }
public Room Room { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public ICollection<Equipment> Equipments { get; set; }
public string Who { get; set; }
}
and I want display a simple Room DropDownlist for Adding and Editing Booking record.
After doing a lots of Google around, it seems that I probably need a DropDopwListFor, but not sure why and how?
Take the following two examples:
#Html.DropDownListFor(
x => x.EquipmentId,
new SelectList(Model.Equipments, "Id", "Text")
)
and:
#Html.DropDownList(
"EquipmentId",
new SelectList(Model.Equipments, "Id", "Text")
)
It is obvious that with the second example the name of the property you are binding the dropdown to is hardcoded as a magic string. This means that if you decide to refactor your model and rename this property Tooling support that you might be using has no way of detecting this change and automatically modifying the magic string you hardcoded in potentially many views. So you will have to manually search & replace everywhere this weakly typed helper is used.
With the first example on the other hand we are using a strongly typed lambda expression tied to the given model property so tools are able to automatically rename it everywhere it is used if you decide to refactor your code. Also if you decide to precompile your views you will get a compiler time error immediately pointing to the view that needs to be fixed. With the second example you (ideally) or users of your site (worst case scenario) will get a runtime error when they visit this particular view.
Strongly typed helpers were first introduced in ASP.NET MVC 2 and the last time I used a weakly typed helper was in an ASP.NET MVC 1 application long time ago.
DropDownListFor will automatically select the selected value by using the specified property:
// Will select the item in model.Equipments that matches Model.EquipmentId
#Html.DropdownListFor(m => m.EquipmentId, Model.Equipments);
Another comment:
Don't have ICollection<Equipment> Equipments in your view model. You should have a property that returns an IEnumerable<SelectListItem>.
When you want to add a view (aspx file) where this DropDownList or DropDownListFor will be inside, rightclick->add view then select "Create a strongly typed view" then in list select Booking class. After that add this page.
You can write in it as follows:
#Html.DropdownListFor(m => m.Equipments , Model.Equipments);
because we add strongly typed view as Booking, you can have:
m => m.ID, m => m.Room, m => m.StartTime
... etc.
In your services you can have methods to take data from database, then use this service's method in your controller to pass data from database to view. You can use ViewData in your controller:
ViewData["Equipments"] = new servicename().getdatalistfromdatabase().AsEnumarable();
Putting AsEnumarable() at the end of your list taken from database makes it IEnumarable.
Then in your view, you can also have :
#Html.DropdownList("MyEquipments" , ViewData["Equipments"]);
A link on ViewData usage:
http://msdn.microsoft.com/en-us/library/dd410596.aspx
I hope that helps you.
DropdownListFor is support strongly type and it name assign by lambda Expression so it shows compile time error if have any error.
DropdownList not support this.
I Have 21 entities with the same structure.
Same Attribute too.
Every entity contains these Attributes :
AreaType
ID
IsActive
LangID
TXT
ModuleType
ID
IsActive
LangID
TXT
...
What I Need to perform a generic Crud. I already know that I need to create a generic repository. My problem is to perform a kind of generic ViewModel.
How can I create a generic View for the Create Form.
I Dont know what I need to pass in the Inherits of the view to be Generic.
... Inherits="System.Web.Mvc.ViewPage<...Dont know>"
Any Idea ?
A common approach this problem is to use ViewModels. This is where you create specific classes to be used as the models in your strongly typed views. These classes would not be the ones created by EF. The ViewModel classes can have a common base that encapulate your common fields. In your data access layer you would need to move data between your ViewModel classes and your EF classes. Things like AutoMapper (from CodePlex) work really well to reduce, if not eliminate, all of the the tedious "left-hand right-hand" coding.
Not too familiar with MVC, but (assuming it fits in with your hierarchy), I think you could create an abstract class which contains the properties you need, e.g.
public abstract class ViewableObject {
public abstract int ID {get; set;}
public abstract bool IsActive {get; set;}
// etc
}
Then implement that with your normal classes (AreaType and so on), e.g:
public class AreaType: ViewableObject{
public override int ID { get; set; }
public override bool IsActive{ get; set; }
}
and make the view use the abstract class.
... Inherits="System.Web.Mvc.ViewPage<ViewableObject>"
One idea is to simply change your underlying tables. Combine AreaType and ModuleType into a single "WhateverType" table that contains a field identifying exactly what type it is. Then when you codegen your classes you'll have exactly one class to deal with.
However, there are other concerns and you should only do this if it makes sense in your application.
I am trying to strongly type a query for 3 ef objects using linq to sql. There are one-to-many relationships with product and category. My classes contain navigation properties and look like this.
public partial class Product
{
public int ID {get;set;}
public string Name {get;set;}
public virtual ICollection<Group> NpGroup {get;set;}
}
public partial class Category
{
public int ID {get;set;}
public string Name {get;set;}
public virtual ICollection<Group> NpGroup {get;set;}
}
public partial class Group
{
public int ID {get;set;}
public int ProductID {get;set;}
public int CategoryID {get;set;}
public virtual Product NpProduct {get;set;}
public virtual Category NpCategory {get;set;}
}
Trying to avoid the string based .Include(), how would I construct a query that returned a group equal to ProductID "1" but also included the names of the product and category?
Something like:
var context = ObjectContext.CurrentObjectContext;
var query = from c in context.Group
where c.ProductID == 1
//Include the names of the product and category of the group record (c.NpProduct.Name etc.)
select c;
I am probably missing the trees through the forest but I can not seem to get the syntax of ObjectContext.LoadProperty (if that is the right way to go).
Any thoughts? Thanks.
First of all, i doubt you are using both L2SQL and EF, so try not to confuse people.
Anyways, with EF - there is two ways to load the navigational properties:
1 - Eager Loading
q.Include("NavPropertyName")
2 - Explicit Load
*After* running your above query - use q.NavPropertyName.Load()
Difference is option 2) causes 2 queries, option 1 causes an inner join on the FK.
I can sympathise with your reluctance to use Include because of the 'magic strings' - i'm not sure why the EF team didn't make them strongly typed, however im sure there was a good reason.
More info on Load/Include here.
HTH
I think we all hate to use the typed string in the .include() statement.
I've started to use a enum to represent the table name, just to avoid spelling errors, etc.
for my database of about 70 tables it took me 10 min. to create the enum and my linq is now something like this:
var context = ObjectContext.CurrentObjectContext;
var query = from c in context.Group.Include(TableEnum.Category.ToString())
where c.ProductID == 1
select c;
Again, not perfect, but at least it's checked by the compiler