ASP.NET MVC UpdateModel - fields vs properties? - asp.net-mvc

I refactored some common properties into a base class and immediately my model updates started failing. UpdateModel() and TryUpdateModel() did not seem to update inherited public properties.
I cannot find detailed info on MSDN nor Google as to the rules or semantics of these methods. The docs are terse (http://msdn.microsoft.com/en-us/library/dd470933.aspx), simply stating:
Updates the specified model instance using values from the controller's current value provider.
SOLVED: MVC.NET does indeed handle inherited properties just fine. This turned out to have nothing to do with inheritance. My base class was implemented with public fields, not properties. Switching them to formal properties (adding {get; set; }) was all I needed. This has bitten me before, I keep wanting to use simple, public fields. I would argue that fields and properties are syntactically identical, and could be argued to be semantically equivalent, for the user of the class.

MVC will bind to properties of the inherited class. The model binder calls something like typeof(yourtype).GetProperties() which returns all the inherited members just fine.
Just tested it out with:
public class PersonBase
{
public string Name { get; set; }
}
public class User : PersonBase
{
public string FavoriteFood { get; set; }
}
"My assumption is the methods are reflecting on the top class only,"
How would that work? The "top" class IS the base class too.

this one made me curious too.
i made a edit form for a class Manager who derives from a Person
(after all, managers are persons too :-))
then in this action method
public ActionResult Edit(Manager manager )
{
return View(manager);
}
which wass called from a view with the Manager (derived type) as strong typed Model variable, when hovering the manager variable, it shows me the base class (it actually said: base: Person ) AND the one extra property for the manager
tried the formcollection too, and that also works:
public ActionResult Edit(FormCollection formCollection )
{
Manager manager = new Manager();
UpdateModel(manager );
return View(manager);
}

Related

How to use an optional, generic parameter on a controller action?

I am working with a BaseController that is used for a variety of entities. They may have int or string primary keys, represented by <TPk>.
E.g.:
[HttpGet]
public ActionResult Create(TPk id)
{
return View();
}
Everything is fine until I try and use TPk as an optional parameter.
[HttpGet]
public ActionResult Create(TPk id = default(TPk))
{
return View();
}
It seems that the 'optional' part isn't working.
So /controller/create/2 is fine, but /controller/create gives me the following error:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Create(Int32)'
The optional works fine with an int or string id. I can call /controller/create/2 AND /controller/create.
But using a generic type argument TPk, the parameterless route no longer works.
What I've Tried
I have tried making the TPk parameter nullable, but it won't compile:
The type 'TPk' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
I have tried changing the parameter name from id to altId as per this question - no joy
I have tried calling the same method, in exactly the same way, but with non-generic parameters. E.g.:
public virtual async Task<ActionResult> Create(int id = default(int))
This worked fine.
I have tried creating a simple new project to isolate this code. (Shown below). This still gives problems with the parameterless version.
Simple Code Test
Controller
public abstract class BaseController<TPk> : Controller
{
public ActionResult Create(TPk id = default(TPk))
{
return View();
}
}
public class NewsController : BaseController<int>
{
}
Entity Classes
public class BaseDataModel<TPk>
{
public TPk Id { get; set; }
public string Title { get; set; }
}
public class PageDataModel : BaseDataModel<string>
{
public string Content { get; set; }
}
public class NewsDataModel : BaseDataModel<int>
{
public DateTime Date { get; set; }
}
Asp.net conventions are heavily based on reflection. So this might explain the behavior. I have not tested if it realy does not work, but I am sure at this state you already tried to create a new project (POC) to preclude any custom code.
Maybe it can be fixed by looking deeper into the routing (method selection) and ModelBinder source code...
I would just create a different DuplicateRecord action instead.
If you do not understand your method without this comment, it is a good indication, that your current code probably smells anyway. (You are doing to much at the same thing):
// duplicates existing record if id is passed in, otherwise from scratch
Extract the shared things to another method (maybe even a service class) and have for each difference a seperate method.
That said, the idea of a generic CrudController is lovely, I tried this myself some years ago. But in trying so I have introduced all sort of generic parameters, strategy patterns, event delegates to make all possibilities possible.
What happens if you need a join?
What happens if you need a transaction?
How do you handle errors?
What happens if your crud logic needs 1, 2, 3 ... additional parameters to decide what to do?
Soft Delete / Hard Delete?
Cascade Delete / Restrict Delete?
What happens if you ...
I have written so much code, it was blessing to revert to the good old non generic code. And if abstracted away in a service, the ActionMethods realy do not need to get big.
public async Task<IActionResult> CreateProduct(CancellationToken ct, ProductCreateModel model)
{
var result = await _productService.CreateAsync(model, ct);
//create response with some helpers... probably some ActionFilters
}
Generics can work ofcorse in a simple crud mapping where each View has exact one Entity, but it does not scale very well. So beaware and think twice about what you realy want ;)

EF, MVC, Model binding, and Navigation properties

I'm using EF5 Code First with :
public class Scenario
{
public int Id { get; set; }
public IList<Client> Clients { get; set; }
}
public class Client
{
public int Id { get; set; }
public string Name {get;set;}
public int VisibilityNumber{ get; set; }
}
I'm directly sending the scenario object to the view (MVC4, without using a viewmodel class - maybe a mistake ?, but a lot less plumbing code). In my view, I use HiddenFor for Scenario.Id, and a for loop to display an EditFor for each client VisibilityNumber.
This is the Controller :
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
if (ModelState.IsValid)
{
Scenario scen=GetScenarioFromDB(id);
TryUpdateModel(scen,formValues);
if (ModelState.IsValid)
SaveToDb(scen);
}
}
After the TryUpdateModel, for each Clients object (which were correctly loaded from DB) :
VisibilityNumber is correctly set
Id is set to 0, which of course is a bad thing
Name is set to null
After looking at the MVC Source code (DefaultModelBinder/UpdateCollection), I can see that when binding to collections, new items are always created.
If I can't fix that, I think I'm going to use a viewModel, and AutoMapper. I assume that the MVC team wanted to force us to use viewModel, rather than directly send EF object.
You should not get scenario from database in your update. Instead, you should take your bound model, attach it (if edited) or add it (if new) to context and then save changes. It's a common scenario called "disconnected entities" (which you, in fact, do have, because you have model that was disconnected when sent to client, and then got back also disconnected).
I "fixed" DefaultModelBinder/UpdateCollection so that it can work with my use case : when the binding is drilling down in the navigation properties, it uses the current object as model (it's easy, since I'm only doing modifications, no insert or delete) : I can take the DefaultModel source code, put my fix in it, and use it as a custom model binder. It's fun, but a bit dirty and over the top.
But I believe the best way is to use a specific ViewModel, using only the properties which are editable, and use AutoMap to map it to my EF hierarchy. BUT : it has the same problem of creating child objects collection.
In the end, I just did some manual mapping for between my View Model and my EF hierarchy : I'm nearly sure I can do something automatic, which could detect if a child item has been modified or inserted or deleted (since every item has a [key] property, but I just don't have the time budget to implement it.

Asp.Net MVC2 TekPub Starter Site methodology question

Ok I've just ran into this and I was only supposed to be checking my emails however I've ended up watching this (and not far off subscribing to TekPub).
http://tekpub.com/production/starter
Now this app is a great starting point, but it raises one issue for me and the development process I've been shown to follow (rightly or wrongly). There is no conversion from the LinqToSql object when passing data to the view. Are there any negitives to this?
The main one I can see is with validation, does this cause issues when using MVC's built in validation as this is somthing we use extensivly. Because we are using the built in objects generated by LinqToSql how would one go about adding validation, like
[Required(ErrorMessage="Name is Required")]
public string Name {get;set;}
Interested to understand the benifits of this methodology and any negitives that, should we take it on, experiance through the development process.
Should this be taken as a guide and we should be using ViewModels? If so should we always use them even in simple cases? And how/where in the application logic does the Entity get converted to a ViewModel?
With entity objects, you could use buddy classes, whereby you create a second class which acts as a metadata provider for your entity. For instance, with a Customer entity generated by Linq-to-Sql, I could create a buddy class like so:
[MetadataType(typeof(CustomerMeta))]
partial class Customer {
}
public class CustomerMeta {
[DisplayName("Forename", Required(ErrorMessage = "Forename is required.")]
public string Forename { get; set;}
}
Entities are generated as partial classes so you can add your own code to them.
Alternatively, you could forego pushing your entity types to your views and create specific models based around the functionality required, for instance I would typically have a User entity, but when I need to create a User, I have something called a CreateUserSpec model:
public class CreateUserSpec
{
[DisplayName("Forename")]
public string Forename { get; set; }
}
Which has a subset of the properties of the User, only those required to create a User. This is the model I would pass to my view, and repopulate from the form data. For instance:
public class AccountController
{
public ActionResult Register() {
return View(new CreateUserSpec());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(CreateUserSpec spec) {
if (!ModelState.IsValid) {
return View(spec);
}
var user = UserFactory.CreateUser(spec);
// Redirect to authorisation page?
}
}

How can I exclude some public properties from being serialized into a JsonResult?

I have a custom viewmodel which serialized using a JsonResult. The ViewModel has some properties which have to be public, but at the same time these properties should not be visible in the resulting Json output.
I've already tried using the [NonSerialized] attribute, but that did not seem to have any effect.
Is there any simple way to do this? Or would I have to code my own result type (in which case I probably won't bother)?
You can put a [ScriptIgnore] attribute on the members that shouldn't be serialized. See ScriptIgnoreAttribute Class in MSDN for an example.
Just create an interface to return instead of a class.
public interface IMyViewModel {
string MyPublicProperty { get; set; }
}
Then create a class that inherits the interface
public class MyViewModel : IMyViewModel {
public string MyPublicProperty { get; set; }
public string MyNotSoPublicProperty { get; set; }
}
And return the interface, not the class, in the Controller Action
public JsonResult MyJson(){
IMyViewModel model = new MyViewModel();
return Json(model);
}
And the resulting JSON will be
{
'MyPublicProperty': ''
}
One of the challenges in client-side scripting is, that if you're changing your classes, you have no idea whether you're destroying the client-side implementation or not. If you use interface types in your JSON, you understand that if you change the interface, you're doing something that potentially may be killing the client side implementation. And it also saves you from double-checking the client side in vain if you're changing something that is NOT in the inteface (thus not being serialized).
Also, many times, your ViewModels might have large collections or complex types in them that you don't necessarily want to output to the client. These might take a long time to serialize or expose information that simply does not belong into the client code. Using interfaces will make it more transparent to know what is being in the output.
Also, using attributes such as [ScriptIgnore] on a property only applies to a specific scenario (JavaScript Serialization) forcing you to face the exact same problem if you're later serializing to XML for example. This would unnecessarily litter your viewmodels with tons of attributes. How many of them you really want in there? Using intefaces applies anywhere and no viewmodel needs to be littered with extra attributes.
Have a look at JSON.NET from James Newton-King. It'll do what you're looking for.
Extend the JavaScriptConverter class to not include properties with the NonSerializedAttribute. Then you can create a custom ActionResult that uses your JavaScriptConverter to serialize the object.
This creates a solid and testable class without having to (re)generate wrapper classes or using anonymous objects.
You can create a wrapper class that exposes only those properties that you want in the JsonResult. In the example below, Cow has 2 properties - "Leg" and "Moo". Suppose you want to only expose "Leg" as a property. Then
Dim cw as CowWrapper = New CowWrapper(c)
will return a wrapper class that only exposes "Leg". This is also useful for things like DataGridView if you only want to display some subset of the properties.
Public Class Cow
Public ReadOnly Property Leg() as String
get
return "leg"
end get
end Property
Public ReadOnly Property Moo() as String
get
return "moo"
end get
end Property
end class
Public Class CowWrapper
Private m_cow as Cow = Nothing
Public Sub New(ByVal cow as Cow)
m_cow = cow
end Sub
m_cow = cow
Public ReadOnly Property Leg() as String
get
return m_cow.Leg()
end get
end Property
end Class
Not exactly the answer you're looking for, but you can cheat Json() using the following code and anonymous classes:
MyModel model = ...;
return Json(new MyModel {model.Prop1, model.Prop2});
I needed the answer to this for ASP.NET Core 6.x and couldn't find it.
I finally found the answer and it is :
[System.Text.Json.Serialization.JsonIgnore]
Here's an example in a class
class Sample{
// Item will not be serialized
[System.Text.Json.Serialization.JsonIgnore]
String Item{get;set;}
// Count will be serialized
int Count{get;set;}
}

MVC Custom ViewModel and auto binding

I have a custom ViewModel defined as :
public class SampleFormViewModel
{
public SampleFormViewModel(SelectList companies, Widget widget)
{
Companies = companies;
Widget = widget;
}
public SelectList Companies { get; private set; }
public Widget Widget { get; private set; }
}
In my Edit POST handler I have the following entry:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(SampleFormViewModel model)
{
Edit form is set up as:
Inherits="System.Web.Mvc.ViewPage<Sample.Web.Models.SampleFormViewModel>"
And it just blows up, not sure what’s going on, has the following error:
No parameterless constructor defined for this object.
Certain I’m missing something really obvious here. Some background, the GET works perfectly and display the dropdown from the SelectList as expected.
I’m guessing the auto-binding back to the custom view model is what is failing but not sure what to do about it.
You need to have a parameterless constructor and I believe that the properties need to have public setters. The default binder creates the object using a constructor that takes no parameters, then uses reflection on the public properties to set values from the form/query parameters.
public class SampleFormViewModel
{
public SampleFormViewModel() { }
public SelectList Companies { get; set; }
public Widget Widget { get; set; }
}
I suspect, though, that what you really want to do is not get the view model, but the underlying Widget model and select list value on form post. I don't think the binder will be able to reconstruct a SelectList on post since it only has the selected value in the parameters.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit( int CompanyID, Widget widget )
{
}
MVC requires, on strongly typed views, that the view can create the class used on that view. This means a constructor without any parameters. And this makes sense. Folks new to MVC will see similar "huh?" issues when they forget/fail to make parameters public and all such related errors that popup when the view attempts to put itself together (as opposed to a compiler error).
But what is "interesting" in this class of parameterless constructor problems is when a property of your class also does NOT have a parameter-free constructor. I guess this is the pessimistic approach?
Having spent some learning time on the SelectList class - a class specific to MVC - I wanted to hopefully help some folks save a few minutes/hours.
This really important tool/class for dropdown list creation, has the following constructors:
public SelectList(IEnumerable items);
public SelectList(IEnumerable items, object selectedValue);
public SelectList(IEnumerable items, string dataValueField, string dataTextField);
public SelectList(IEnumerable items, string dataValueField, string dataTextField, object selectedValue);
..and therefore, if these are properties on your class (the one used for the view), MVC will give you the elusive "No parameterless constructor" error.
BUT, if you create something like a helper class, cut-n-paste the exact code from your original class, and then make that helper class a parameter (NOT a get/set) on your original class; you're good to go.
And in this manner, you can use a single view for gets and posts. Which is more beautiful :)
Personnally, I'd have either created the compiler to recognize the associations and requirements of strong typed views, or let the dropdown (or other "customer" of the SelectList) just fail to work rather then wonder if there's a specific level of recursive checking on paramerterless constructors.
Thankfully, the current version seems to only be top-level. Feels like a hack and I hope it's by design.
HTH.

Resources