I've got a viewpage with the inherits part looking like
Inherits="System.Web.Mvc.ViewPage<User>"
and a ViewUserControl with the inherits part looking like
Inherits="System.Web.Mvc.ViewUserControl<Address>
where User class (shortened) essentially looks like:
class User {
public virtual Address address { get; set; }
}
Now, if I try:
Html.RenderPartial("Address", Model.address);
then I potentially get some strange behaviour. In my case, it turns out Model.address was null Instead of passing null to the control, it looks like the framework tried to pass Model (i.e type=User) and was throwing an error (unexpected type).
Disregarding the fact that my address was null, is that behaviour of defaulting to the parent object a bug, or expected behaviour. If it is expected behaviour then can someone please explain why? I don't get it.
Very often these kinds of questions result in the OP making a mistake elsewhere, and the error isn't reproducible. I tried recreating your exact scenario, and what do you know? You're actually right :) I created a simple Parent / Child relationship and had the View be of type child, and the partial of type child.Parent that was null, and I didn't get a null reference, I did in fact get the type mismatch error. In fact, even if explicitly passed null on to the view:
Html.RenderPartial("MyPartial",null);
or even with a direct cast:
Html.RenderPartial("MyPartial",(Parent)null);
it still gave the mismatch error.
My guess and this really is a total guess, is that when it detects that the object being passed in is null, it just defaults to using the Model of the actual view itself. I'm not sure either if this is by design or a bug, but that's the only thing that makes sense.
I believe you should avoid passing null Models to your Views. Use default/empty Model if your Model is null. This should work:
Html.RenderPartial("Address", Model.address ?? new Address { /* OPTIONAL: default values */});
Related
Say I have a request model object:
public class RequestModel
{
public string? Id { get; set; }
// other properties...
}
And I want to use that model for this example controller method:
public ResponseModel ExampleMethod(RequestModel request)
{
// FluentValidation validator
_validator.ValidateAndThrow(request);
// This method does not accept a nullable type
_dependency.DoSomething(request.Id); // Causes "Possible null reference argument for parameter" error
return new ResponseModel();
}
In this case it's correct for the Id property to be marked as nullable (because in theory the request could not include it). The validator will ensure that the properties are not null. However if I want to use this property in DoSomething() then I will get compiler warnings due to the fact that the Id could be null. The only solution I can find is to map the external request object to some internal version where the properties are not nullable.
However this would require the mapping to essentially be performing validation (by throwing some kind of exception during mapping if a null is encountered) which feels like a violation of separation of concerns:
public ResponseModel ExampleMethod(RequestModel request)
{
// FluentValidation validator
_validator.ValidateAndThrow(request);
// Map the request to an internal object - throw an exception if mapping fails due to null properties
var internalModel = _mapper.Map<InternalModel>(request);
// This method does not accept a nullable type
_dependency.DoSomething(internalModel.Id); // No more error
return new ResponseModel();
}
Not sure if I'm missing something here or if this is the only way to solve the problem. I can't make the property non-nullable as then it would require a default value (eg. empty string, or even worse - null! or default!) which would make it impossible to determine whether the property was missing in the request or was genuinely passed as an empty string. I believe something like this proposal may resolve the issue as then I would be able to indicate to the compiler that I'm expecting these non-nullable properties to be provided upon initialization (by model binding) rather than with a constructor. Am I missing some aspect of nullable reference types here that would make this any easier to deal with?
You have a model with an optional value. Within a user-defined method you validate that this value is defined. The compiler can't determine this behaviour and thous the warning.
To help the compiler you could use the null-forgiving operator like this:
_dependency.DoSomething(internalModel.Id!);
Instead of using allowing null and afterwards check this manually you should maybe use better the available model validation within ASP core. Within your model you should better mark your property with the RequiredAttribute and also manually calling a fluent validator is not needed if you register it within your startup code with .AddFluentValidation(). If your model and validator is correctly marked you can within your Controller method do something like this and you're done:
if(!ModelState.IsValid)
return BadRequest(ModelState);
The only solution I can find is to map the external request object to some internal version where the properties are not nullable.
This sounds like a great approach to me. It's very common to separate request models from your core business models. The role of a controller action (which this appears to be) is largely to coordinate the translation of outside requests to and from the core business logic.
You might even want to have your dependency use your internal model rather than the Id, to avoid primitive obsession. If your dependency "knows" that the number it's being given should represent the Id of a specific type of model, it may be less error-prone to make it impossible for someone to give it a number that has nothing to do with that model type (or an ID directly from an input model that they forgot to validate).
_dependency.DoSomething(internalModel);
However this would require the mapping to essentially be performing validation (by throwing some kind of exception during mapping if a null is encountered) which feels like a violation of separation of concerns
Validation of inputs is an implied part of any method's contract, including any method that returns a value. Does int.Parse() violate separation of concerns by throwing exceptions on bad inputs before returning an int?
If anything, you're violating separation of concerns by using a single model class to represent two different concepts (input versus domain model), which can change for different reasons.
There's only one "concern" involved with validating the input model and converting it into a known-valid domain model. That implies that you should probably separate that concern into its own method/class.
public ResponseModel ExampleMethod(RequestModel request)
{
var internalModel = _requestValidator.Validate(request);
_dependency.DoSomething(internalModel);
return new ResponseModel();
}
The fact that your _requestValidator is using fluent model validation and automapper is an implementation detail that this level of code (a controller action, e.g.) shouldn't have to worry about. Maybe you'll change that some day to use explicit hand-coded mapping. You'd want your unit tests to test that validation/mapping independent of this class's logic.
I have the following line in one of my views (cshtml files):
#Html.Partial("_ProviderRatingWidget", Model.RatingInfo)
In my lab, everything seems to be working fine. But in deployment, we are seeing the following error:
System.InvalidOperationException: The model item passed into the dictionary is of type 'ProviderContactInfo', but this dictionary requires a model item of type 'RatingCategorySummaryModel'.
Model is explicitly of type ProviderContactInfo, and Model.RatingInfo is explicitly of type RatingCategorySummaryModel. And so how can this line ever pass type ProviderContactInfo?
Can anyone see any possible circumstances where this line could produce that error?
EDIT
Oh, I should point out that the line above is also in a partial view. (The partial view is loading another partial view.) That might be related to the problem.
I have verified that the current model will be passed if the property you are trying to pass is null. You can get around the issue with this:
#Html.RenderPartial("_ProviderRatingWidget", new ViewDataDictionary(Model.RatingInfo));
Hope that helps!
mmm I not test, but maybe this work:
#Html.Partial("_ProviderRatingWidget", ((RatingCategorySummaryModel)Model.RatingInfo))
Regards.,
k
I've got a browser sending up JSON but it only includes the properties of a given model that have been changed. So once the WCF DataContractJsonSerializer does it's work I have an object that will have perhaps only the ID and Description fields populated.
Attaching this to the DbContext as is will result in the description field being updated but all the other fields being set to their types default value in the database. This is because if WCF doesn't see the property specified in the JSON then it'll skip over it, meaning the property in the instance will just use the types default value as per the auto-implemented property.
So this means that I need to decide on which fields have been passed up without having access to the JSON itself. In fact it could be XML on the wire so all I can work from is this partly serialized object.
What seems most logical is to use null as the special value that means this property hasn't been serializd over. So in the constructor of the POCO model I set all the properties to null.
In the Update method I then use this serialized object as a stub. I have to walk each property and if the value isn't set to null then I set it's state to modified. As far as I can tell this is working without any side effects but I'm just not sure that this is the way to do something like this.
One limitation it does add is that the client can no longer intentionally set a property to null as that update would be lost. One way around this is to have a special int value that can be set to represent null in the database and perhaps an empty string to represent null in the database and have code in the update to look for these special values and then set the entity property to null. Far from ideal and likely to be prone to bugs.
Here is the code I currently have to process the update. I would really really appreciate advice as to a better, perhaps more obvious way of doing this.
To Summerise: How can I tell which properties on an model instance have been set by the DataContractSerializer/DataContractJsonSerializer and which are just using default values from it's constructor. Using special values is problematic as the client might want to set something to an empty string, or to 0 or -1 or indeed to null.
public T Update(T obj)
{
var entity = ds.Attach(obj);
// For each property in the model
foreach (var p in typeof(T).GetProperties())
{
// Get the value of the property
var v = p.GetValue(obj, null);
// Assume null means that the property wasn't passed from the client
if (v == null)
continue;
// Set this property on the entity to modified unless it's ID which won't change
if (p.Name != "ID")
dc.Entry(entity).Property(p.Name).IsModified = true;
}
dc.SaveChanges();
return entity;
}
UPDATE: Using Hammerstein's answer below to have self tracked models, I've updated my update function as below. Unfortunately due to my use of the Required attribute on the models for pre-save validation EF throws a wobbly when using a stub instance that contains nulls for the non modified values. You would think that EF would realise as part of it's validation that some fields are set to not modified but alas it doesn't so I'm forced to do a read and then update that. Actually this might be a good candidate for a separate question to post to try and avoid the read.
public virtual T Update(T obj)
{
var entity = ds.Find(obj.ID);
((TrackedModel)obj).Modified.ForEach(p => {
var prop = dc.Entry(entity).Property(p.PropertyName);
prop.CurrentValue = p.NewValue;
prop.IsModified = true;
});
dc.SaveChanges();
return entity;
}
My solution to this problem was a tracked change model, I created an abstract base class that had a list of strings, then for each property on my model, I called a method NotifyChanged( "MyProperty") which added the property to the list.
Because model binding will only set the fields that have been posted back, you should get an accurate list of fields that changed.
Then I loop through the list, and set the values on my entity.
Not clean, but it worked for my purpose.
Update: My solution did require me to get away from auto-properties and to hand write them. In the setter, after setting the value, I call NotifyChanged. I'm using this with MVC regular model binding, I don't think I have a working example of passing an object as JSON and deserializing. You could look at JSON.NET, controlling the serialization/deserialization I believe you can tell it to ignore default property values etc.
I have this property in my view model:
[DisplayName("Region")]
public int? RegionId { get; set; }
I pass my view model to my controller, and it fails at ModelState.IsValid if RegionId is null. If I pass an integer to it, it works fine.
The error message is:
The value 'null' is not valid for Region
I've also tried calling this before I check ModelState.IsValid, but I get the same error:
if (viewModel.RegionId == null)
viewModel.RegionId = (int?)null;
What's the problem here? Why can't I assign null to something that is nullable?
Here's another possible explanation.
I am AJAX POSTing to an MVC action method using jQuery like so:
$.post('url/here',dataObject);
jQuery will turn the data object into a query string. I use the default model binder to push the values into my ViewModel. Model validation fails with "The value 'null' is not valid for field name". But the property is a nullable int. After inspecting the raw POST I find that the data object I am POSTing is serialized into a query string that looks like this:
Field1=null&Field2=value&Field3=value
and not this:
Field1=&Field2=value&Field3=value
So model validation is understandably complaining about assigning the string literal 'null' to my nullable int. One solution is to check for NULLs before POSTing. I wish I had time to dig in more but I just switched to sending a JSON representation of the data object which correctly handles the null values.
In your Global.asax.cs file, in the Application_Start method, add the following line:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
The issue is that, even for a nullable value type, by default, a "Required" attribute is added by the default ValidatorProvider.
See here: ModelState validation fails for nullable types
The string "null" may be being passed. In my scenario I updated to an empty string "" and resolved this error.
I think the issue is not assigning null, it's that the consuming code does not except a null value. The assignment is perfectly valid (although your null check is inverted, and the cast is redundant).
viewModel.RegionId = null;
As an aside you can use HasValue to check for null on a nullable type. Although in reality this it's no different to a null check, just looks a bit cleaner:
if (viewModel.RegionId.HasValue)
{
// do something
}
I had a problem with this.
My problem with this is that I was setting the offending property to null explicitly in the POST. So, akin to what you have, the request would've looked like:
RegionId = null
In my case, it was as simple as omitting the explicit declaration (e.g., don't say it's null, just don't send any data)
I have a feeling the problem lies somewhere out of the sample code you have shown. I have created a sample application that just creates a form and has a nullable field, but it works as expected. I would start eliminating other variables in your setup until you can to a working ViewModel with a nullable property, and you should be able to identify what is causing the problem.
I have a partial view that inherits from ViewUserControl<Guid?> - i.e. it's model is of type Nullable<Guid>. Very simple view, nothing special, but that's not the point.
Somewhere else, I do Html.RenderPartial( "MyView", someGuid ), where someGuid is of type Nullable<Guid>. Everything's perfectly legal, should work OK, right?
But here's the gotcha: the second argument of Html.RenderPartial is of type object, and therefore, Nullable<Guid> being a value type, it must be boxed. But nullable types are somehow special in the CLR, so that when you box one of those, you actually get either a boxed value of type T (Nullable's argument), or a null if the nullable didn't have a value to begin with. And that last case is actually interesting.
Turns out, sometimes, I do have a situation when someGuid.HasValue == false. And in those cases, I effectively get a call Html.RenderPartial( "MyView", null ). And what does the HtmlHelper do when the model is null? Believe it or not, it just goes ahead and takes the parent view's model. Regardless of it's type.
So, naturally, in those cases, I get an exception saying: "The model item passed into the dictionary is of type 'Parent.View.Model.Type', but this dictionary requires a model item of type 'System.Guid?'"
So the question is: how do I make MVC correctly pass new Nullable<Guid> { HasValue = false } instead of trying to grab the parent's model?
Note: I did consider wrapping my Guid? in an object of another type, specifically created for this occasion, but this seems completely ridiculous. Don't want to do that as long as there's another way.
Note 2: now that I've wrote all this, I've realized that the question may be reduced to how to pass a null for model without ending up with parent's model?
<% Html.RenderPartial("MyView", someGuid ?? new Guid()); %>
UPDATE:
Using editor and/or display templates in ASP.NET MVC 2.0 you can achieve the desired result. Place a Guid.ascx file in the Shared/EditorTemplates folder and include it like this:
<%= Html.EditorFor(x => someGuid) %>
or if the guid is a property of the main model:
<%= Html.EditorFor(x => x.SomeGuid) %>
Now if you put a <%= Model.HasValue %> inside the partial you can get false but not with RenderPartial.