I have a viewmodel consisting of a handful of properties:
public class FooDataViewModel : IValidatableObject
{
/* Several Properties working as expected */
public ICollection<Foo> Foos {get; set;}
/* Validation working as expected */
}
Foo holds a few fields and works as expected:
public class Foo
{
/* Only a few basic properties, works as expected */
}
But I have some objects that are Foobar:
public class FooBar : Foo, IValidatableObject
{
/* Just a few more properties and some conditional validation */
}
I created a custom editor template for both Foo and FooBar. For my edit view, I simply use '#Html.EditorFor(m => m.Foos)' and editors for both Foo and FooBar are displayed. But when I submit to my HttpPost method, only Foo objects are created, and the FooBar specific data is lost. How can I make sure that that the FooBar objects are created?
How can I make sure that that the FooBar objects are created?
You will need to include this information in the request and write a custom model binder that is able to use this information and instantiate the proper type. I have illustrated the concept here: ViewModel with List<BaseClass> and editor templates
Related
I would like to add attributes to my controller methods which can be inspected using reflection.
I can see how to do this by writing a filter, and I will write an empty filter if that's the only way to achieve what I want, but all I really want is a reflection-visible attribute that can be used to generate documentation. Example:
[OperatorFriendlyDescription("Begin a new message from a letter template and set initial properties.")]
public ActionResult Create(string editorName, int mastKey, ...)
Is there a way to get my OperatorFriendlyDescription attribute without writing a new filter?
(Alternatively, is there some other approach or documentation feature that would allow me to set an operator friendly name for individual controller methods and retrieve this with reflection?)
There is no magical way you can use an OperatorFriendlyDescription attribute without defining it, but if it doesn't need Filter functionality, don't inherit from FilterAttribute.
If you look at the declaration of the MVC FilterAttribute, you will see it is just a specialized System.Attribute.
public abstract class FilterAttribute : Attribute, IMvcFilter
From the MSDN documentation we can verify System.Attribute is the base for all attributes.
Since you have no special functionality needed, inherit from that instead.
An example Attribute in C#
public class ArbitraryAttribute: Attribute
{
public string ArbitraryData { get; private set; }
public ArbitraryAttribute(string arbitraryData)
{
ArbitraryData = arbitraryData;
}
}
Yes. Any attribute that allows being placed on that type of method/class/property, etc., can be used. This class has to inherit from System.Attribute or another class that inherits from that.
VB example, should be very similar in C#. This one can only be placed on methods due to the attribute target attribute. leave off the AttributeUsage attribute for the attribute to be used anywhere.
<AttributeUsage(AttributeTargets.Method)>
Public Class OperatorFriendlyDescription
Inherits System.Attribute
Public Property Description As String
Public Sub New(description As String)
Me.Description = description
End Sub
End Class
I had a Model with a property that is an abstract class
public class MyModel
{
public Foo Property { get; set; }
}
I have several implementations of Foo, each with their own EditorTemplate. Each implementation also has fields unique to it. Let's call these AFoo, BFoo, and CFoo.
Now, when I post my form, the model binder will try to create these all as Foo. Obviously, this will not work, because Foo is abstract.
Short of writing Model.GetType() to a hidden field, then using a custom model binder, is there a way to tell the default model binder how to correctly construct the correct type?
I am having data annotation validation issue with model containing model of foreign object.
Lets say
Class Foo
{
public virtual Int Id {get; set;}
[Required]
public virtual String Name {get; set;}
}
Class Bar
{
public virtual Int Id {get; set;}
[Required]
public virtual String AnotherName {get; set;}
public virtual Foo foo {get; set;}
}
FOO is NOT mandatory/required while creating BAR.
But when i try to check the ModelState in HTTPPOST of BAR it say model state is invalid, and report the column of FOO as missing.
[HTTPPOST]
public ActionResult SaveBar(BAR bar)
{
if (ModelState.IsValid)
SaveBar(bar);
}
I have set FOO as null, as tried TryUpdateModel, but still didnt help.
EDIT
I am dealing with entities and not view models
And also.. How to tell model binder not to check the FOO when binding BAR...
EDIT
modified example..
It appears that the model binder is instantiating Foo as a member of the Bar object when you hit 'SaveBar()'. Thus, the attribute validation on Foo fires, just as you would expect.
Here's what I would do: remove the attribute validation and just go a different route, maybe like a custom validation method that you call from the controller actions for normal "Foo" operations. "SaveBar()" can check Foo for a default state and decide to call that validation or not, depending.
It doesn't work the way you think it is. The ModelState is invalid because you declared the fields of Foo as required. If your view does not accept and pass values for Foo.Id and Foo.Name then you will get an invalid ModelState.
Don't use [Required] on your model's id. I'm not sure whether you're dealing with entities or view models here, but in general, the id should be allowed to be unset. In the case of entities, the id will not be set until it is saved to the database (typically) and in the case of a view model, you might be representing an entity that has not previously been saved (and thus has no id).
[Required] has only two real uses, so you should understand exactly what those are and then only apply the attribute accordingly:
1) On an entity, [Required] will imply NOT NULL. It really only makes sense on strings since every other scalar type is NOT NULL by default.
2) On a view model being used to accept form data from a POST. This will require that the field not be blank. If your id is not a form field being presented to the user for input (which I highly doubt it is), then it should not be required.
#hgrathi, what you are not understanding is that as soon as you made Name in Foo Required, Foo is now Required under Bar.
One way to solve this is to create a custom model binder by implementing IModelBinder.
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);
}
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;}
}