I'm getting a very strange exception.
I have a model with a TimeSpan property and try to create a view.
public class Clock {
[DataType(DataType.Time)]
[DisplayFormat(DataFormatString = #"{0:hh\:mm}", ApplyFormatInEditMode = true)]
public TimeSpan Time {get;set;}
}
#Html.EditorFor(model => model.Time)
That is what I get
[InvalidOperationException: The model item passed into the dictionary is of type 'System.TimeSpan', but this dictionary requires a model item of type 'System.String'.]
System.Web.Mvc.ViewDataDictionary`1.SetModel(Object value) +321071
System.Web.Mvc.ViewDataDictionary..ctor(ViewDataDictionary dictionary) +377
System.Web.Mvc.WebViewPage`1.SetViewData(ViewDataDictionary viewData) +48
I've used this technique in another project and it works, but in my current project it fails and I don't know my. Maybe I've missed something or something is disabled.
When using #Html.EditorFor(), MVC first looks to see if it can find a template using the default convention (or one you may have defined in a custom ViewEngine).
If it can't find one -- and in your case, you didn't have one defined -- then it uses built in templates. In the case of TimeSpan, it was trying to use a template for a String type, which resulted in the exception you saw.
You will need to explicitly define a TimeSpan.cshtml template, typed with #model TimeSpan.
My answer is not so much an answer as a workaround, since I'm not sure of the reason for the exception, but for me it works if I do a clean checkout from TFS.
Related
I initialize my strings to blank, not null. Which worked fine in MVC 1, using tryupdatemodel in that any text entries that had no text in them would be set to a blank string, in MVC 2 RC2 apparently (at least from my testing) it sets them to null / nothing. So now I'm getting errors from my validation layer which requires those to be blanks not null. Another problem with it that I have found is that now via reflection it calls every property on my objects including the ones I've not specified in a bind include statement and even readonly properties that could not be set.
Any one have an idea of the easiest way to get around these problems without totally changing all my code? Or should I just suck it up and
About properties that you haven't bound - asp.net mvc 2 rc has this change from input validation to model validation, here is how it works now
I suppose the issue you have with string is also because of changes made in RC2, check out the release notes.
To fix the string defaulting to null instead of "" I had to do this first:
Public Class NullToEmptyStringModelBinder
Inherits DefaultModelBinder
Protected Overrides Sub SetProperty(ByVal controllerContext As System.Web.Mvc.ControllerContext, ByVal bindingContext As System.Web.Mvc.ModelBindingContext, ByVal propertyDescriptor As System.ComponentModel.PropertyDescriptor, ByVal value As Object)
If value Is Nothing AndAlso propertyDescriptor.PropertyType Is GetType(String) Then
value = ""
End If
MyBase.SetProperty(controllerContext, bindingContext, propertyDescriptor, value)
End Sub
End Class
And then add to the application start this:
ModelBinders.Binders.DefaultBinder = New NullToEmptyStringModelBinder
I'm getting the following InvalidOperationException:
The parameter conversion from type 'System.String' to type 'System.Runtime.Serialization.ExtensionDataObject' failed because no type converter can convert between these types.
In a Post action on my ASP.Net MVC2 page, but I'm really not sure what it's referring to. I'm using data annotation validation:
public class FamilyPersonMetadata
{
[Required(ErrorMessage = "Name Required")]
public String Name;
[Required(ErrorMessage = "Date of Birth required")]
[DateTime(ErrorMessage = "Invalid Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d")]
public DateTime DateOfBirth;
}
[MetadataType(typeof(FamilyPersonMetadata))]
public partial class FamilyPerson
{
}
And my view inhertis from a ViewPage with a subtype of FamilyPerson. I just create controls with names matching those of FamilyPerson and then submit the form, but for some reason my ModelState is invalid and the above error is apparently the reason. I'm quite perplexed as to the nature of the error. Similar code is working for other views and actions.
Could someone point me in the direction of things to look at that might cause this?
Regarding [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d")]
"{0:d" should be "{0:d}"
Here's an actual explanation of the problem, and how to solve it: http://www.shawson.co.uk/codeblog/mvc-strongly-typed-view-returns-a-null-model-on-post-back/comment-page-1/
Short summary: Don't give your view parameters the same names as model fields, unless they represent the same value (and have the same type).
It seems to have gone away on its own. Weird.
This may help someone:
I had this exception being thrown because i had mulitiple forms in my view.
However one of the forms did not explicitly set the 'action' attribute. That is, i was using this constructor:
#using (Html.BeginForm())
Instead of this one:
#using (Html.BeginForm("ACTION_METHOD", "CONTROLLER", FormMethod.Post, null))
This would result in the incorrect parameters being posted with the Form. Spoecifically, the business model object was being included on the Form when it shouldn't be. In turn, .Net was then trying to convert a System.String to a business model object when it shouldn't be attempting such a conversion.
The solution is to use the later overloaded method and ensure that the correct 'action' attribute is being set for your Form upon postback.
FYI: To inspect the 'action' attribute of your Form, use FireBug and inspect the HTML, find the Form element and the 'action' attribute will be there with all the parameters that will be posted back to the serber when that Form is submitted.
I am having a weird issue in ASP.NET MVC with objects not being updated with UpdateModel when passed a formCollection. UpdateModel does not appear to be working properly when the object being updated is created through reflection.
Scenario: I have an application which has approximately 50 lookup tables--each of which includes exactly the same schema including typical fields like id, title, description, isactive, and createdon. Rather than build 50 views, I wanted to have a single view which could display the data from all of the lookup tables. I created an Interface called IReferenceEntity and implemented it in each of the POCOs representing my lookup tables.
Using this interface, I am able to easily populate a view with a record from the lookup table. (I pass the items to the view via the following.)
System.Web.Mvc.ViewPage<MyNamespece.IReferenceEntity>
From the database to the view, every thing works perfectly.
However, when I attempt to update the model on post, I am running into some problems.
If I explicitly declare an object reference like the following, every thing works perfectly and the values of my object are updated with the values from my form. Hence, I can then update the database.
AccountStatus a = new AccountStatus();
UpdateModel(a, formCollection.ToValueProvider());
Unfortunately, hard coding the object type would completely defeat the reason for using an interface.
(A primary objective of the application is to be able to dynamically add new tables such as lookup tables without having to do anything "special". This is accomplished by reflecting on the loaded assemblies and locating any classes which implement a specific interface or base class)
My strategy is to determine the concrete type of the object at postback and then create an instance of the type through reflection. (The mechanism I use to determine type is somewhat primitive. I include it as a hidden field within the form. Better ideas are welcome.)
When I create an instance of the object using reflection through any of the following methods, none of the objects are being updated by UpdateModel.
Type t = {Magically Determined Type}
object b = Activator.CreatorInstance(t);
UpdateModel(b, formCollection.ToValueProvider());
Type t = {Magically Determined Type}
var c = Activator.CreatorInstance(t);
UpdateModel(c, formCollection.ToValueProvider());
Type t = {Magically Determined Type}
IReferenceEntity d = Activator.CreatorInstance(t);
UpdateModel(d, formCollection.ToValueProvider());
Note: I have verified that the objects which are being created through relection are all of the proper type.
Does anyone have any idea why this might be happening? I am somewhat stumped.
If I was really "hard up", I could create factory object which would many instantiate any one of these reference entity/lookup objects. However, this would break the application's ability to allow for new lookup tables to be added and discovered transparently and is just not quite as clean.
Also, I could try deriving from an actual ReferenceEntity base class as opposed to an interface, but I am doubtful whether this would make any difference. The issue appears to be with using reflection created objects in the modelbinder.
Any help is appreciated.
Anthony
Augi answered this on ASP.NET forums. It worked with only a couple of minor modifications. Thank you Augi.
The problem is that [Try]UpdateModel methods allow to specify model type using generic parameter only so they don't allow dynamic model type specification. I have created issue ticket for this.
You can see TryModelUpdate method implementation here. So it's not difficult to write own overload:
public virtual bool TryUpdateModelDynamic<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class
{
if (model == null)
{
throw new ArgumentNullException("model");
}
if (valueProvider == null)
{
throw new ArgumentNullException("valueProvider");
}
//Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
IModelBinder binder = Binders.GetBinder( /*typeof(TModel)*/model.GetType());
ModelBindingContext bindingContext = new ModelBindingContext()
{
Model = model,
ModelName = prefix,
ModelState = ModelState,
//ModelType = typeof(TModel), // old
ModelType = model.GetType(),
// new
//PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
binder.BindModel(ControllerContext, bindingContext);
return ModelState.IsValid;
}
Does your IReferenceEntity contain setters on the properties as well as getters? I would think that the last sample would work if the interface had property setters, though you'd have to cast it to get it to compile.
Type t = {Magically Determined Type}
IReferenceEntity d = Activator.CreatorInstance(t) as IReferenceEntity;
UpdateModel(d, formCollection.ToValueProvider());
Normally the reason that it won't set a property on a class is because it can't find a public setter method available to use via reflection.
Just a quick "another thing to try":
UpdateModel(d as IReferenceEntity, formCollection.ToValueProvider());
Not sure if that will work, and I haven't tried it myself, but it's the first thing that came to mind.
If I get a chance later I'll peek at the Default Model Binder code and see if there's anything in there that is obvious...
I have a search form with a DateTime search criterion, plus some other criteria:
<form method="get" action="/app/search">
<input type="text" value="13/01/2010" name="BeginDate"/>
<input type="text" value="blah" name="SomeOtherCriterion"/>
<form>
So I have a Search controller with a default Action (let's call it Index) and with a SearchCriteria parameter.
public class SearchController
{
public ActionResult Index(SearchCriteria searchCriteria) {//blah }
}
public class SearchCriteria
{
public DateTime BeginDate {get; set;}
public string SomeOtherCriterion {get; set;}
}
Now if I want to create an ActionLink, passing in a SearchCriteria value, thus:
Html.ActionLink("Search", "Index", searchCriteria)
I get the BeginDate query string parameter in US format. Looking on Google and poking around in System.Web.Routing using Reflector it seems to be because it uses the InvariantCulture, so there's nothing I can do about it.
It seems like noone has asked this question before so I guess I'm doing something very stupid.... Please help!
EDIT: Pass in SearchCriteria to ActionLink rather than anonymous object to show why I can't just do the custom ToString() myself.
Given that the framework appears to be hard-coded to handle this piece of data using InvariantCulture, I don't think there's much you can do to make it work transparently.
There is one ugly option - download the MVC source and rip out the code for all the offending classes from Route down to ParsedRoute to create your own RouteBase implementation that does what you need.
If I absolutely had to keep the DateTime declaration on the SearchCriteria class, then that's the route (sorry for the pun) I would choose.
However, a far easier solution would be to change your SearchCriteria class to use a slightly different declaration for the DateTime field, based on a type like this:
public class MyDateTime
{
public DateTime Value { get; set; }
//for passing MyDateTime in place of a DateTime without casting
public static implicit operator DateTime(MyDateTime instance) { return instance.Value; }
//so you can assign a MyDateTime from a DateTime without a cast
//- e.g. MyDateTime dt = DateTime.Now
public static implicit operator MyDateTime(DateTime instance) { return new MyDateTime() { Value = instance }; }
//override ToString so that CultureInfo.CurrentCulture is used correctly.
public override string ToString()
{
return Value.ToString(CultureInfo.CurrentUICulture);
}
}
In theory you should be able to roll out this change without too much fuss.
The big work could be if you have a lot of code that uses members (e.g. .Days etc) of the DateTime instance in SearchCriteria: you either have to reproduce those members on MyDateTime, wrapping around the inner DateTime Value or change all the code to use .Value.Member.
To avoid issues related to Regional Settings and "Culture",
I treat date and time as separate unbound fields and then
assemble them into DateTime in my Controller.
Example:
Year [ ] Month [ ] Day [ ]
I always present separate textboxes for year, month, and day, in that order so that there can be no confusion between U.S. format (month/day/year) and more or less the rest of the world's format (day/month/year).
Can you provide a formatted date in your ActionLink? Try this:
Html.ActionLink("Search",
"Index",
new {BeginDate =
DateTime.Now.ToString("d", new CultureInfo("pt-BR");})
Of course this changes BeginDate to a string instead of a DateTime... but maybe that will work for you?
We use ISO ("s" in a format string -- YYYY-MM-DDTHH:MM:SS) format for this. It works correctly, and JavaScript can handle it as well.
Perhaps you could use a Model Binder to format and parse the date? Just re-read the article and noticed that it does not format the date...Probably not going to work out. I'll leave the answer though in case it provides any unintentional inspiration :)
poking around in System.Web.Routing using Reflector it
seems to be because it uses the
InvariantCulture
Are you realy shure about this? The parts of Modelbinding and UrlBuilding I checked used CurrentCulture. Can you check what happens if you set the CurrentCulture before rendering the link?
Get the ASP.NET MVC 1.0 book written by Scott Hanselman, Scott Guthrie, Phil Haack, and Rob Conery. They actually do this exact scenario in the book. They use a specific route. I am looking at it right now on page 216.
They do it by breaking up day, month, and year. Then it is your responsibility to use those values as they come back.
I am trying to implement Optimistic Locking in an asp.net MVC application, as well as provide audit trailing.
The audit framework relies on being able to call DataContext.GetModifiedMembers during SubmitChanges, which makes good sense, i guess.
The optimistic locking uses ROWVERSION timestamps, serialized to base64 and put in a hidden field in the view.
My Edit action looks like this:
[AcceptVerb(HttpVerb.Post)]
public ActionResult Edit(MyType myType)
{
context.MyTypes.Attach(myType, true);
context.SubmitChanges(ConflictMode.FailOnFirstConflict);
}
When doing this, the DataContext.GetModifiedMembers will always return ALL the properties on MyType, rather than just the ones that are changed between the database and the values provided, which breaks the auditing.
Specifically it returns every property as having been changed from their new value to their new value, so its not even like I can do anything clever to the list.
I tried loading the object first, before attaching it, but this gives a duplicate key exception.
I then tried using UpdateModel, i.e.
[AcceptVerb(HttpVerb.Post)]
public ActionResult Edit(int id, FormCollection col)
{
var mt = context.MyTypes.Single( mt => mt.id = id);
UpdateModel(mt);
context.SubmitChanges(ConflictMode.FailOnFirstConflict);
}
This works with the auditing, but fails the optimistic locking.
Rather than a ChangeConflictException i get an InvalidOperationException because the UpdateModel is changing the concurrentTS field (which apparently is readonly).
What am I doing wrong?
Progress so far consists of doing the last part, and catching InvalidOperationException and looking for the text "Value of member 'ConcurrencyTimestamp'", and rethrowing that as a ChangeConflictException.
That seems to do the trick, but it is not pretty.