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?
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 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;}
}
My model is like this
public class MyModel
{
string ID {get;set;}
string Title {get;set;}
MyOtherModel Meta {get;set;}
}
How to define custom model binder for type (MyOtherModel) so when default binder binds MyModel it calls custom model binder for 'Meta' property.
I registered it in App start like:
ModelBinders.Binders[typeof(MyOtherModel)] = new MyCustomBinder();
but this doesn't work. Any idea or any good article with more infor regarding to model binders?
There is an article about collections that touches a bit the complex type mapping stuff:
Collections and a bit about complex types
In the other hand this article could give you some useful tips:
http://odetocode.com/Blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
I suggest you as a workaround to use a model binder for MyModel class, it's not a perfect solution but you can refactor it easily once you discover a better solution. : )
Actually, If you will put in some Edit/Create View with Model of your MyModel class something like this:
<%= Html.TextBox("Meta.Prop1") %>
where Prop1 is property of your MyOtherModel class,
then
UpdateModel(model);
will populate textbox value to your custom submodel property. And back, textbox value would be populated with that submodel value as well.
So, once you update your current model, you are updating submodels too.
Is there a way to use something like this: System.Web.Mvc.ViewUserControl<DateTime>? I get an exception that the type is a value type, not a reference type. What is the proper way to resolve this? Thanks.
Edit
What I am trying to accomplish is having a control that takes a DateTime to render a calendar. I want to pass in the DateTime from my ViewData using "dot notation" for MVC.
Edit 2
I heard/seen that some MvcContrib projects might have this capability, but I can't seem to find it again.
There is no way to resolve this - only workarounds. You cannot include Value types as TModel in ViewUserControl as TModel has a constraint to be a reference type.
The easy workaround is to wrap your value type in a class as your model.
class MyModel {
public DateTime? DateTime {get;set;}
}
By defining your own class like MyModel above, you can now pass a DateTime to your views, like so
ActionResult MyActionMethod() {
var db = new MyDataContext();
var dbThing = db.Things.Where(t=> t.DateTimeProperty>=DateTime.Now).First();
return View("myView", new MyModel{DateTime = dbThing.DateTimeProperty});
}
Your view of course will need to define MyModel as it's model type, like so
public partial class MyView:ViewUserControl<MyModel> {
//snip
}
And inside your View, simply refer to the DateTime property to access the DateTime.
<%=Model.DateTime%>