Model binding to an inheriting class - c#

I have the following classes:
public abstract class InputVariableVm
{
public InputVariableVmType Type { get; set; }
}
[KnownType(typeof(BoolInputVariableVm))]
public class BoolInputVariableVm : InputVariableVm
{
public bool Value { get; set; }
public BoolInputVariableVm(string name, bool value)
{
Value = value;
Type = InputVariableVmType.Bool;
}
}
[KnownType(typeof(StringInputVariableVm))]
public class StringInputVariableVm : InputVariableVm
{
public string Value { get; set; }
public StringInputVariableVm(string name, string value)
{
Value = value;
Type = InputVariableVmType.String;
}
}
In my Web API controller I'm trying to bind to an object of InputVariableVm (String or Bool).
However the object is always null - but when I remove the "abstract" keyword from the base class it inserts the base class (but without the concrete implementation, thus missing the Value property).
What could be the cause of this?
By the way, I'm well aware that writing a custom model binder would solve this but I would like to avoid doing this if possible.

This is just how the default model binder works - the type you put as the action parameter is the type the binder will attempt to instantiate, it has no idea that really you want a derived type instantiated behind the scenes. FWIW the reason it's null in the first scenario is because you can't instantiate an abstract class hence why removing it then works.
By the way, I'm well aware that writing a custom model binder would solve this but I would like to avoid doing this if possible.
Unfortunately, there is no way around it - you are going to need a custom model binder.

Related

Is there a problem with C# new modifier with different types?

I have the following abstract class AggregateId<T> class now, but the type of the Value property there used to be a Guid. I changed it because I needed it to be a lot more generic, the problem is if I change the class in order to add a new Generic type, like AggregateId<T, TValue> it would make me perform a lot of refactoring, and I gonna end adding a lot of "noise" in the code.
The solution for this problem was change the AggregateId.Value to object type and them
using the new modifier with a different type for the classes that inherit from it.
public abstract class AggregateId<T> where T : IAggregateRoot
{
public object Value { get; }
protected AggregateId(object value)
{
Value = value;
}
}
public class SomeClassId : AggregateId<SomeClass>
{
public new string Value { get; }
public MarketId(string value)
: base(value)
{
Value = value;
}
}
Now, I tried to find some information about if it is OK or why would be bad, do what I have done, but I found nothing anywhere, so my question is if there is any problem to make it the way I did?

Can not create instance of an inherited class from an abstract class

I have a class inherited from an abstarct class. On razor, when I create instance of child class, I got this error as is shown in image:
Cannot create an abstract class
But StuffRegisterSearchParameterVM is not an abstract class. Why this happen?
Controller
public ActionResult SearchRegistration(SearchParameterVM model)
Model
abstract public class SearchParameterVM
{
public string FromDate { get; set; }
public string ToDate { get; set; }
public int? MainTestRegisterId { get; set; }
public int TestTypeId { get; set; }
public bool IsForAnsweringPage { get; set; }
}
public class StuffRegisterSearchParameterVM : SearchParameterVM
{
public int? StuffId { get; set; }
public string Code { get; set; }
}
You can not use abstract class as a parameter of action, because asp.net mvc does not know anything abount posted object type, it trys to create an argument type, and this type is abstract.
So, replace it this concrete class or create special binder.
When you define the action:
public ActionResult SearchRegistration(SearchParameterVM model)
That defines a method that MVC will call based on your routes when an http request is made to the server. That http request probably contains only parameters like you would have if you had a web form. MVC model binding simply creates an instance of the class specified in the parameter to the action in C# and tries to set the property values based on the http parameters passed in the http call. This call could be from a view action like you have, from a static html page, from a program, or anywhere else you can make an http call. When it is an abstract class, it cannot create an instance of it.If you had 3 child classes based on your abstract class, MVC would have no way to tell which type to create.
You can check out this question for some more information.
So how would you determine what concrete class should exist in memory when a call to that action is made, given only parameter names with different values? You could create different routes and actions that had different types in their parameters. You could also check those parameters and create different concrete classes based on the passed parameters. For instance if you wanted to use a certain class based on if the 'code' value is passed, , you'll either have to create your own IModelBinder which could determine which concrete class based on the passed query parameters:
public class MyModelBinder : IModelBinder {
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
// create different concrete instance based on parameters
ValueProviderResult code = bindingContext.ValueProvider.GetValue("Code");
if (code.AttemptedValue != null) {
// code is passed as a parameter, might be our class
// create instance of StuffRegisterSearchParameterVM and return it
}
// no Code parameter passed, check for others
}
}
Then you have to tell in your startup that you have a special model binder
for your abstract class:
ModelBinders.Binders.Add(typeof(SearchParameterVM), new MyModelBinder());
Or you could do something in your action to determine what type to create and use TryUpdateModel to set the values:
public ActionResult SearchRegistration() {
SearchParameterVM model = null;
if (Request.Parameters["code"] != null) {
model = new StuffRegisterSearchParameterVM();
TryUpdateModel(model); // check return value
}
}

Pass Json object as object of type System.Object or Interface not working

Class:
public class ClassNameA :ISomeInterface {
}
public class ClassNameB :ISomeInterface {
}
From javascript:
var reqP = { 'Id': id, 'Name':name };
var ReqParams = { 'ReqParams': reqP };
var obj = { 'ClassNameA': ReqParams };
makeAjaxCall("POST",
JSON.stringify(obj), '/ControllerName/someMethod/', 'html',
Action method looks like:
public ActionResult someMethod(object obj){
// call comes to this method but obj is not populated.
}
public ActionResult someMethod(ISomeInterface obj){
// call comes to this method but throws exception.
// Exception : Cannot instantiate interface. but i am passing class object.
}
from JavaScript I will pass object of a concrete class type which implements ISomeInterface so that I can have multiple implementations. Concrete Class can of any one of the two types.
Any Suggestions?
That won't work. The model binder needs a concrete type to be able to create an instance and bind the values.
object is a concrete type and can be created using Activator.CreateInstance, but it doesn't have any properties that will match the data you receive.
The interface (or abstract types) cannot be created, since they're not concrete types. It's a simple as that.
If the JSON contained some hint about the type, it migth be possible to implement a custom model binder to create the instances for you. You can read more about the model binding process here.
The default model binder is not going to be able to figure out what to do. Let's just go through the exercise with some sample interfaces to show why. Say you have this:
public ActionResult SomeMethod(ISomeInterface obj)
{
// ...
}
...and say you have these two implemenetations:
class ClassA : ISomeInterface
{
public string Name { get; set; }
public string Phone { get; set; }
}
class ClassB : ISomeInterface
{
public string Name { get; set; }
public string Email { get; set; }
}
...and say this JSON is posted:
{ "Name": "Some Value" }
How would the model binder know which class to use? Would it search all defined classes in all assemblies to find all implementations of the interface? And if so, even if it had smart selection logic based on properties, how would it select between ClassA or ClassB, both of which are compatible?
What you may want to do is either use a type like Dictionary<string, object> that you know will be compatible, dynamic, or go with a concrete class that is a union of everything you need. Alternately, you can create a custom model binder with its own logic for selecting the class you want to instantiate. See this question for more details.

Call Property by Name

I have a class with over 100 uniquely named properties and more than 20 child-classes, sometimes in lists. Below is a greatly simplified illustration of what I mean:
public class classA
{
public String PropertyA1 { get; set; }
public int PropertyA2{get;set;}
public List<classB> myList;
public classC myClass { get; set; }
public void SetProperty(String PropertyName)
{
// Match property name to property in this class or child class.
}
}
class classB
{
public String PropertyB1 { get; set; }
public bool PropertyB2 { get; set; }
}
class classC
{
public String PropertyC1 { get; set; }
}
I would like to do two things that may or may not be possible. The first thing I need to do is iterate through every public property, including those of child classes and classes in a list, and translate the values. I know I can accomplish the parsing by serializing to xml and parsing through the results. I even have the code in place to convert to xml, as the function of the class is to build an xml object. However, I am worried that parsing through the xml might be much more expensive than accessing the properties through reflection. Can reflection be used in this manner, and would it be quicker than modifying the xml?
The other thing I would like to do is access any property passing the property name into a method. I realize I would need a separate method for accessing classes within lists, and may have to convert the list to a dictionary. The question is, would this be possible, and would the code only need to be in the parent class, or would each of the child classes need to repeat the code?
Method that will set the property with the given name:
public void SetProperty(String propertyName, object value)
{
this.GetType().GetProperty(propertyName).SetValue(this, value);
}
A few things about the implementation:
The type used is the dynamic actual type of the object, that will find members that are in derived classes (as long as the object is of the derived type of course).
The property info has no idea of what object it came from, so this must be passed in again to the SetValue() call.
The second part of your question, to iterate through a list of properties, can be solved by using GetProperties() to get all the properties of the object, including inherited ones:
var properties = this.GetType().GetProperties();

C# class design with Generic structure

This might be a simple one, but my head is refusing to wrap around that, so an outside view is always useful in that case!
I need to design an object hierarchy to implement a Parameter Registration for a patient. This will take place on a certain date and collect a number of different parameters about a patient (bloodpressure, heartrate etc). The values of those Parameter Registrations can be of different types, such as strings, integers, floats or even guids (for lookup lists).
So we have:
public class ParameterRegistration
{
public DateTime RegistrationDate { get; set; }
public IList<ParameterRegistrationValue> ParameterRegistrationValues { get; set; }
}
public class ParameterRegistrationValue
{
public Parameter Parameter { get; set; }
public RegistrationValue RegistrationValue { get; set; } // this needs to accomodate the different possible types of registrations!
}
public class Parameter
{
// some general information about Parameters
}
public class RegistrationValue<T>
{
public RegistrationValue(T value)
{
Value = value;
}
public T Value { get; private set; }
}
UPDATE: Thanks to the suggestions, the model has now morphed to the following:
public class ParameterRegistration
{
public DateTime RegistrationDate { get; set; }
public IList<ParameterRegistrationValue> ParameterRegistrationValues { get; set; }
}
public abstract class ParameterRegistrationValue()
{
public static ParameterRegistrationValue CreateParameterRegistrationValue(ParameterType type)
{
switch(type)
{
case ParameterType.Integer:
return new ParameterRegistrationValue<Int32>();
case ParameterType.String:
return new ParameterRegistrationValue<String>();
case ParameterType.Guid:
return new ParameterRegistrationValue<Guid>();
default: throw new ArgumentOutOfRangeException("Invalid ParameterType: " + type);
}
}
public Parameter Parameter { get; set; }
}
public class ParameterRegistrationValue<T> : ParameterRegistrationValue
{
public T RegistrationValue {get; set; }
}
public enum ParameterType
{
Integer,
Guid,
String
}
public class Parameter
{
public string ParameterName { get; set; }
public ParameterType ParameterType { get; set;}
}
which is indeed a bit simpler, but now I'm wondering, since the IList in ParameterRegistration points to the abstract ParameterRegistrationValue object, how will I be able to get the actual value out (since its stored on the sub-objects)?
Maybe the whole generic thing is indeed not quite the way to go after all :s
If you don't know the final set of parameter and the corresponding type of each parameter then the generics probably won't help - use object as a parameter value type.
Furthermore iterating through the list of parameters will be a pain since you'll have to examine the type of each item in order to determine how to treat the value.
What are you trying to achieve with generics ? Yes, they are cool (and going for boxing/unboxing is probably not a best idea), but in some cases you might want to use object instead (for both simplicity and flexibility).
-- Pavel
What you might want to introduce is an abstract base class for RegistrationValue<T> that is not generic, so that your ParameterRegistrationValue class can hold a non-generic reference, without needing knowledge of the type involved. Alternatively, it may be appropriate to make ParameterRegistrationValue generic also, and then add a non-generic base class for it instead (so that the list of values in ParameterRegistration can be of different types.
1st way:
public abstract class RegistrationValue
{
}
public class RegistrationValue<T> : RegistrationValue
{
public RegistrationValue(T value)
{
Value = value;
}
public T Value { get; private set; }
}
And now your code should compile.
Once you have a non-generic base class, I'd also move any members of the generic class that don't depend on the generic type parameters up into this base class. There aren't any in this example, but if we were instead modifying ParameterRegistrationValue to be generic, I'd move Parameter up into the non-generic base class (because it doesn't depend on the type parameter for RegistrationValue)
May be, you should use public RegistrationValue RegistrationValue, where T - is type, using in generic. For example, T - is String or other class or struct.
Or you should make class ParameterRegistrationValue as generic, to use generic argument in the field RegistrationValue.
I believe you want to have a collection of instances of different RegistrationValues-derived classes and be able to iterate it and for to have different type for each element. That's rather impossible.
You'll still need to cast each element to the type you know it is, because iterating the collection will return references to your base type (ParameterRegistrationValue - this one specified by IList type parameter). So it won't make any real difference from iterating over non-generic object list.
And if you can safely do that casting for each parameter (you know all the types), you probably don't need collection like this at all - it'll be better to have a class that encapsulates all the parameters in one type, so that you can call it with strong types, with IntelliSense etc. like this:
public class ParameterRegistration
{
public DateTime RegistrationDate { get; set; }
public PatientData PatientData { get; set; }
public Guid Identifier { get; set; }
// ...
}

Categories

Resources