UpdateModel won't properly convert a boolean value - c#

I have a custom object called S2kBool that can be converted to and from a regular Boolean object. Basically, it allows my application to treat boolean values in my legacy database the same way it treats C# booleans. Then problem is, when I attempt to use a check box to set the value of an S2kBool property, it fails.
Code like this works:
public class MyClass {
public S2kBool MyProperty { get; set; }
}
MyClassInstance.MyProperty = true;
But it's almost like UpdateModel is expecting an actual bool type, rather than an object that can be converted to a bool. I can't really tell, however, since the exception thrown is so vague:
The model was not successfully updated.
How can I get around this? Do I need a custom ModelBinder?
Thanks!

While Charlino's solution is clever and will work, I personally wouldn't like the idea of "dirtying" up my domain entities with an extra property just for this purpose. I think you had the answer up top already: a custom modelbinder. Something like:
public class S2kBoolAttribute : CustomModelBinderAttribute, IModelBinder
{
public override IModelBinder GetBinder()
{
return this;
}
public object BindModel( ControllerContext controllerContext, ModelBindingContext bindingContext )
{
ValueProviderResult result;
return bindingContext.ValueProvider.TryGetValue( bindingContext.ModelName, out result )
? (S2kBool)result.ConvertTo( typeof( bool ) )
: null;
}
}
And then you can modify your controller action to look like:
public ActionResult Foo( [S2kBool]S2kBool myProperty ){
myClassInstance.MyProperty = myProperty;
SaveToLegacyDb(myClassInstance);
return RedirectToAction("Bar");
}
If you put a bit more work into the modelbinder you could get it to work with the binder being globally registered - but the implementation I gave you above should work for cherry-picking values out when needed.

You could have an additional bool property of type bool that when set changes the value of your S2kBool property.
public class MyClass {
public S2kBool MyProperty { get; set; }
public bool MyPropertyBool {
get
{
return (bool)MyProperty;
}
set
{
MyProperty = value;
}
}
}
You then just have the MyPropertyBool in your html form and the modelbinder won't freak out about it's type.
I use this technique for properties like Password & HashedPassword where Password is the property from the html form that the ModelBinder binds to and in the Password's setter it sets HashedPassword to the hash of it which is then persisted to the database or what ever.

Related

ASP MVC - Cast ViewModel Object to another type

I have a ViewModel like this :
public class Column {
public string Name;
public string Type;
public object Content;
}
Content can be an int, string... Depending on the Type I call the specific EditorTemplate : Column_int, Column_string... no problem.
I do however have a type GeoPoint I want to use too, with the corresponding EditorTemplate Column_GeoPoint. The problem is that in my controller, when the ViewModel gets posted, when I do :
GeoPoint geoPoint = ((GeoPoint) mViewModel.Content);
I get a cast exception.
Any ideas on how to incorporate an object inside another one in the ViewModel ?
If you receive proper data in format you expect, you need to provide custom cast method/helper/extension.
See here how to write custom explicit cast: https://msdn.microsoft.com/en-us/library/xhbhezf4.aspx
The default model binder will not be able to convert an object to GeoPoint via cast. I've not used GeoPoint before, but a quick google doesn't show how this can be easily converted from a string, perhaps that's not relevant if your Editor handles it correctly
You'll need to convert it correctly/manually (via code).
Your best option is to always use the correct strong types in your viewmodel, ie:
public class ColumnViewModel {
public string Name;
public string Type;
public string ContentString;
public int ContentInt;
public GeoPoint ContentGeoPoint;
}
ie do not use a weakly-typed, vague "object" property to handle different scenarios.
Then map that to your DTO in the controller:
public class Column {
public string Name;
public string Type;
public object Content;
}
public ActionResult PostAction(ColumnViewModel model)
{
var dto = new Column();
switch (model.Type)
{
case "int":
dto.Content = dto.ContentInt;
case "geo":
dto.Content = dto.ContentGeoPoint;
break;
// etc
}
}
It would be better to have multiple view models, so there aren't unused properties (which can be confusing to maintain in the future) - a perfect case for inheritance. Or use generics for this property as suggested elsewhere.
You could always be lazy and add a ToGeoPoint() method in your Column viewmodel.
It would look something like:
public class Column {
public string Name;
public string Type;
public object Content;
public GeoPoint ToGeoPoint() {
GeoPoint point = new GeoPoint();
point.Name = this.Name;
point.Type = this.Type;
point.Content = this.Content;
return point;
}
}
Of course this assumes that a GeoPoint and a Column have the same structure. Even if they don't it should be easy to see how to modify this method to allow for that.

Passing a context containing properties to a TypeConverter

I'm looking for a way of passing additional information to a TypeConverter in order to provide some context for conversions without creating a custom constructor.
That extra information passed would be original object (known at compile time as an interface) that contains the property that I am converting. It contains properties of its own like Id that are useful for lookups to convert related information.
I've had a look at the documentation for ITypeDescriptorContext but I haven't found a clear-cut example of how to implement that interface. I'm also not convinced it's the tool I need.
At the moment, in my code I'm calling:
// For each writeable property in my output class.
// If property has TypeConverterAttribute
var converted = converter.ConvertFrom(propertyFromOriginalObject)
propertyInfo.SetValue(output, converted, null);
What I'd like to do is something like.
// Original object is an interface at compile time.
var mayNewValue = converter.ConvertFrom(originalObject, propertyFromOriginalObject)
I'd like to be able to use one of the overloads to do what I need so that any custom converters can inherit from TypeConverter rather than a base class with a custom constructor as that would make life easier with dependency injection and use DependencyResolver.Current.GetService(type) from MVC to initialise my converter.
Any ideas?
The method you want to use is clearly this overload: TypeConverter.ConvertFrom Method (ITypeDescriptorContext, CultureInfo, Object)
It will allow you to pass a pretty generic context. The Instance property represents the object instance you're working on, and the PropertyDescriptor property represents the property definition of the property value being converted.
For example, the Winforms property grid does exactly that.
So, you'll have to provide your own context. Here is a sample one:
public class MyContext : ITypeDescriptorContext
{
public MyContext(object instance, string propertyName)
{
Instance = instance;
PropertyDescriptor = TypeDescriptor.GetProperties(instance)[propertyName];
}
public object Instance { get; private set; }
public PropertyDescriptor PropertyDescriptor { get; private set; }
public IContainer Container { get; private set; }
public void OnComponentChanged()
{
}
public bool OnComponentChanging()
{
return true;
}
public object GetService(Type serviceType)
{
return null;
}
}
So, let's consider a custom converter, as you see it can grab the existing object's property value using one line of code (note this code is compatible with standard existing ITypeDescriptorContext like the property grid one although in real life scenarios, you must check the context for nullity):
public class MyTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
// get existing value
object existingPropertyValue = context.PropertyDescriptor.GetValue(context.Instance);
// do something useful here
...
}
}
Now, if you have this custom object being modified:
public class MySampleObject
{
public MySampleObject()
{
MySampleProp = "hello world";
}
public string MySampleProp { get; set; }
}
You can call the converter like this:
MyTypeConverter tc = new MyTypeConverter();
object newValue = tc.ConvertFrom(new MyContext(new MySampleObject(), "MySampleProp"), null, "whatever");

Custom Boolean Parameter Binding

I have a WebApi method, like this one:
public string Get([FromUri] SampleInput input)
{
//do stuff with the input...
return "ok";
}
The input is defined like this:
public class SampleInput
{
// ...other fields
public bool IsAwesome { get; set; }
}
As it is, it works OK: if I pass &isAwesome=true in the query string, the parameter is initializes with the value true.
My problem is that I'd like to accept both &isAwesome=true and &isAwesome=1 as true values. Currently, the second version will result in IsAwesome being false in the input model.
What I tried, after reading the various blog posts on the subject, was to define an HttpParameterBinding:
public class BooleanNumericParameterBinding : HttpParameterBinding
{
private static readonly HashSet<string> TrueValues =
new HashSet<string>(new[] { "true", "1" }, StringComparer.InvariantCultureIgnoreCase);
public BooleanNumericParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor)
{
}
public override Task ExecuteBindingAsync(
ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken)
{
var routeValues = actionContext.ControllerContext.RouteData.Values;
var value = (routeValues[Descriptor.ParameterName] ?? 0).ToString();
return Task.FromResult(TrueValues.Contains(value));
}
}
... and register it in Global.asax.cs, using:
var pb = GlobalConfiguration.Configuration.ParameterBindingRules;
pb.Add(typeof(bool), p => new BooleanNumericParameterBinding(p));
and
var pb = GlobalConfiguration.Configuration.ParameterBindingRules;
pb.Insert(0, typeof(bool), p => new BooleanNumericParameterBinding(p));
None of these worked. My custom HttpParameterBinding is not being called and I still get the value 1 translated to false.
How can I configure WebAPI to accept the value 1 as true for Booleans?
Edit: The example I presented is intentionally simplified. I have a lot of input models in my application and they contain many boolean fields that I would like to be handled in the manner described above. If there was just this one field, I would not have resorted to such complex mechanisms.
Looks like decorating the parameter with the FromUriAttribute just skips the parameter binding rules altogether. I made a simple test replacing the SampleInput input parameter with a simple bool:
public string Get([FromUri] bool IsAwesome)
{
//do stuff with the input...
return "ok";
}
and the boolean rule is still not getting called (IsAwesome is coming as null when you call &isAwesome=1).
As soon as you remove the FromUri attribute:
public string Get(bool IsAwesome)
{
//do stuff with the input...
return "ok";
}
the rule gets called and the parameter correctly bound.
The FromUriAttribute class is sealed, so I think you're pretty much screwed - well, you can always reimplement it and include your alternate boolean binding logic ^_^.

Container for properties values

When .NET 4.5 was released i started using such great Attribute as CallerMemberName. It's easier to understand code, developers can write it faster also. It's like a snippet, not only a feature for debug/test purposes.
So I have a question. Is it normal to create and use something like this?
public class PropertyStore
{
Dictionary<string, object> data = new Dictionary<string,object>();
ViewModelBase modelBase;
internal PropertyStore(ViewModelBase _base)
{
modelBase = _base;
}
public void SetValue<T>(T value = default(T), [CallerMemberName] string prop = "")
{
T prev = GetValue<T>(prop);
if ((prev == null && value == null) || (prev != null && prev.Equals(value))) return;
data[prop] = value;
modelBase.OnPropertyChanged(prop);
}
public T GetValue<T>([CallerMemberName] string prop = "")
{
if (!data.ContainsKey(prop))
data[prop] = default(T);
return (T)data[prop];
}
}
Class-helper, that makes other class more readable, and also we have list of our properties without need to use Reflection.
The usage is:
public class SampleClass : ViewModelBase
{
PropertyStore PropertyStore;
public SampleClass ()
{
PropertyStore = new PropertyStore(this);
}
public string Key
{
get { return PropertyStore.GetValue<string>(); }
set { PropertyStore.SetValue(value); }
}
public DateTime Date
{
get { return PropertyStore.GetValue<DateTime>(); }
set { PropertyStore.SetValue(value); }
}
public bool IsSelected
{
get { return PropertyStore.GetValue<bool>(); }
set { PropertyStore.SetValue(value); }
}
}
The class ViewModelBase here simply implements INotifyPropertyChanged interface.
As I understand, this approach is something like Microsoft Dependency Properties, but I don't need all power of DependencyObject class, and I don't want inherit it.
With something like this I can use Binding, because it's enough to implement INotifyPropertyChanged, also we have no fields (as for me, i try to use properties smarter, than using fields directly (however, there is no problem to use Dictionary directly ^_^))
Sorry for my bad English... Not main language and not much practice.
Another Sample (after moving Methods to base class)
public class SampleClass : ViewModelBase
{
public string Key
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
public DateTime Date
{
get { return GetValue<DateTime>(); }
set { SetValue(value); }
}
public bool IsSelected
{
get { return GetValue<bool>(); }
set { SetValue(value); }
}
}
No diff with Microsoft's WPF Property System.
Only feature you'll get with it is an ability to access property values via Dictionary.Get|Set methods.
You can get this ability with field based implementation of INotifyPropertyChanged. You can access property values by its name using dictionary, with property name to precompiled delegate mapping like it done in Yappi project.
var dateValue= Property<SampleClass>.Get<DateTime>(this,"Date");
Property<SampleClass>.Set<DateTime>(this,"Date",DateTime.Now);
Both can be rewritten as extension methods.
Nice idea, property bag without reflection and it will even work with obfuscation.
I don't see major problems with it but you may consider the following:
The prop parameter is optional so potentially a bug can be introduced by given a value in the call.
Value types will get boxed.
Access to the fields is relatively more expensive, can be a factor more expensive as you have much more code in a simple get (especially with boxing).
Dictionary takes more space than the number of properties you keep in (especially with boxing).
Each property also stores a string of the property name adding to the overhead.

PostSharp aspect for property setters, calling generic method

We have a base object we use for some MVC-like system, where each property in a descendant is written like this:
public String FirstName
{
get { return GetProperty<String>("FirstName", ref _FirstName); }
set { SetProperty<String>("FirstName", ref _FirstName, value); }
}
This is done both for debugging purposes and for notification and validation purposes. We use the getter to alert us of cases where code that has explicitly flagged what it is going to read (in order for the base class to be able to call it only when those properties change) and gets it wrong, and we use the setter for property change notifications, dirty-flag handling, validation, etc.
For simplicity, let's assume the implementation of these methods looks like this:
protected T GetProperty<T>(String propertyName,
ref T backingField)
{
return backingField;
}
protected Boolean SetProperty<T>(String propertyName,
ref T backingField,
T newValue)
{
backingField = newValue;
return true;
}
There's more code in both of these of course, but this code is not relevant to my question, or at least I hope so. If it is, I'll modify the question.
Anyway, I'd like to write a PostSharp aspect that automatically implements the calls for me, on automatic properties, like this:
public String FirstName { get; set; }
Is there anyone out there that has some idea how I would go about doing this?
I have made OnMethodBoundaryAspect classes myself, but the art of calling the generic implementation with a ref parameter eludes me.
Here's the two classes, I'd like to augment the TestObject class to automatically call the correct method on property get and set.
public class BaseObject
{
protected T GetProperty<T>(String propertyName,
ref T backingField)
{
return backingField;
}
protected Boolean SetProperty<T>(String propertyName,
ref T backingField,
T newValue)
{
backingField = newValue;
}
}
public class TestObject : BaseObject
{
public String FirstName
{
get;
set;
}
public String LastName
{
get;
set;
}
}
Edit: Posted on PostSharp forum as well.
It should be very simple. You override the OnEntry and set the return value based on your own code. At the end you use:
eventArgs.ReturnValue = GetValue(x,y);
eventArgs.FlowBehavior = FlowBehavior.Return;
which will effectively intercept the original Get/Set calls.
Refer to this blog which shows the cache aspect using the same pattern...

Categories