mvc3 database first model defaults - c#

I have a database first design. I want to have default values for my model so that a user can just leave an editor textbox blank for one of my nullable fields. EF doesn't seem to want to just throw null into the database, so I was hoping I could set default values to null for my nullable attributes.
Ie I want to insert null into my database for an attribute if the textbox is left blank when submitted.
I've read you can add a contructor to a partial class to do what I want. So you would basically have:
public partial class MyClass{
public MyClass()
{
field1 = null; //this would be the default value for field1
}
}
The only problem is the autogenerated partial class for the model I'm working on already has a constructor, so I can't add a constructor to a different (permanent) partial class. I don't want to update the autogenerated partial class because it will just be overwritten when I update my edmx from the database.

The default value of the string should already be null, so I think you are taking the wrong path.
The problem is a textbox will always be passed with a form post, even if it is blank, which will result in an empty string in your controller rather than a null value, even if you find a way to explicitly set your string to null in your initial class.
Instead, check the value of the string in your controller after the form is posted back, and if it is empty, set it to null.
if (String.IsNullOrEmpty(formModel.MyString)) formModel.MyString = null;
Either that, or create a default model binder to check the value of the field during model binding, and not set it if it is empty.

Related

ASP.NET core binding fails due to null field value marked with [JsonIgnore]

An ASP.NET controller accepts an object which looks similar to this one:
public class Data
{
public string Text {get;set;}
[JsonIgnore] public int Length => Text.Length;
}
Text property value is optional. However, when it is null then MVC fails during model binding. For some reason, the MVC binding code tries to access the Length field and gets an exception due to the null Text.
I naively assumed that JsonIgnore attribute would be taken into account by MVC or at least it would not try to deserialize the read-only property. How can I hide this property from MVC binding? Is there an attribute that works as JsonIgnore that MVC understands?
The platform is .net core 2.1
EDIT:
The marked answer pointed me in the right direction. I disabled validation based on information from this page Model validation in ASP.NET Core MVC and Razor Pages
In ConfigureServices startup object I added this line:
services.AddSingleton<IObjectModelValidator>(new NullObjectModelValidator());
and created NullObjectModelValidator
public class NullObjectModelValidator : IObjectModelValidator
{
public void Validate(ActionContext actionContext,
ValidationStateDictionary validationState, string prefix, object model)
{
}
}
It is not the right solution in every case but if data does not need validation then this approach works.
Firstly - if you pass the data from form to the Controller by usual way it is not passed as Json, but as FormData, in Url Encoded Form (example: Text=&Length=4). So JsonIgnore is irelevant here.
Next thing is, that ModelBinder does not try to bind value to the Length (as it is read-only). It sets only the Text to the null value. Then it tries to apply some default validation, which includes traversing the object, getting its values and apply validation attributes.
And here comes your error as described in comment and #nurdyguy answer: property Length accesses property Length of null object. So solution here is:
public int Length => Text?.Length ?? 0;
Another solution, which I do not recommend is to skip the validation on the field by:
[ValidateNever]public int Length => Text?.Length ?? 0;
But this only hides the error in your code, which is still there.
BTW: if you really need sometime to exclude some property from model binding, use [BindNever] attribute on the property (but here it does not solve your problem, as described before).
What the [JsonIgnore] tag does is tells the model binder to ignore that value when de/serializaing JSON to a poco. That is functioning correctly and is not the problem here. Your problem is that [JsonIgnore] public int Length => Text.Length; will fail when Text is null because Text.Length throws a null reference exception. This is totally independent of the [JsonIgnore]. You defined the property Length as being set based on a calculation. Whether the [JsonIgnore] tag is there or not this value must calculate because it is part of the model.
One way to fix your issue is to use null coalescence:
[JsonIgnore] public int Length => Text?.Length ?? 0;
This way, when Text is null the Text?.Length returns null and then the ?? checks for null and returns 0. Another option would be to let Length be a nullable int:
[JsonIgnore] public int? Length => Text?.Length;
So now the Length will be null when Text is null but have a value when Text is not null. The downfall is that you'll have to do work elsewhere dealing with the value being a nullable int vs regular int.

Is it possible to configure application for replace DateTime.MinValue to null?

I work on a big ASP.NET MVC application. This app contains many forms with date time controls. Values of this controls is optional. Also in database field where store values from this controls can be null.
Date-time controls returns Min date (0001/1/1) if users not fill it.
I'm see often next exceptions in logs The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value. I know how fix it
For example.
Before fix:
DecisionOfLiquidationData= model.DecisionOfLiquidationData,
CompletionOfLiquidationData = model.CompletionOfLiquidationData
After fix:
DecisionOfLiquidationData= model.DecisionOfLiquidationData==DateTime.MinValue ? null: model.DecisionOfLiquidationData,
CompletionOfLiquidationData = model.CompletionOfLiquidationData == DateTime.MinValue ? null : model.DecisionOfLiquidationData
But application contains many code that's look like before fix.
My question is next. Is any way to decide this problem globally using web.config or global.asax? Which ideas?
My suggestion is to add a default value to the constructor for your model objects to set the value you want as default.
E.g.
public MyDbEntity()
{
myDateProperty = new DateTime(1900,1,1);
}
This could be quite a bit of typing if you have a lot of model objects with DateTimes that need to be changed, but at least you will only need to do it once per object, and then your done.
Edit: Note that when using Model First Entity Framework, that you will have to create a partial class and declare the constructor there in order to avoid losing your changes when the model code is regenerated.
you can use nullable ? with datetime like
public DateTime? DecisionOfLiquidationData {get;set;}
what it will do is set the value to default null if no value is assigned

boolean model propertie's values not bind in POST method

I have table record as follows
this how those two properties defined in model classes
public bool? Report_Users { get; set; }
public bool? Innovation_Discussion_User { get; set; }
this is Form syntax that used to populate Boolean values in checkboxes
#Html.CheckBox("Report User", Model.Report_Users.GetValueOrDefault(), new { #value = "false" }) Report User
#Html.CheckBox("Innovation Discussion User", Model.Innovation_Discussion_User.GetValueOrDefault(), new { #value = "false" }) Innovation Discussion User
This is check boxes view of that form
but once I submit , I can see in model those two properties values getting null in POST method
How can I bind this check-boxes values to Boolean property fields, properly ?
There a a number of issues with you implementation.
Your CheckBox("Report User", ...) method is generating a checkbox (and associated hidden input) with name="Report" and your model does not contain a property named Report. Because no value is posted back for Report_Users, its value will always be its default (null) in the POST method. In order to bind to bool? Report_Users then it would need to be CheckBox("Report_User", ...) (underscore not space).
Changing that however means the value of Report_User now always be false instead of null because you have added new { #value = "false" }. The CheckBox() method generates 2 inputs, <input type="checkbox".. value="True" /> and <input type="hidden".. value="False" /> in order to correctly bind to your property. You have now overridden it to generate <input type="checkbox".. value="false" /> so a value of false is posted even if the checkbox is checked.
But the real issue with your code is that your property is bool? (nullable) and CheckBox() is designed to bind to bool. typeof bool? has 3 states (true/false/null) whereas a checkbox has 2 states (on/off or true/false). Its not clear why your database property is nullable, but if you cannot change it, then (as always), use a view model with a bool property. In the GET method, when mapping your data model to the view model, you can use
myViewModel.ReportUser = myDataModel.Report_User.GetValueOrDefault();
and now you can build you view correctly using the strongly typed helper
#Html.CheckBoxFor(m => m.ReportUser)
#Html.LabelFor(m => m.ReportUser, "Report User") // use a label
// or add [Display(Name="Report User")] and use #Html.LabelFor(m => m.ReportUser)
What to learn from this:
If your model is not binding, always check the html your generating
(the name attributes of the form controls must match the name of
the property)
Always use the strongly typed xxxxFor() HtmlHelpers to generate
form controls. Not only do you get intellisense, it ensures that the
correct name attributes are generated, and any errors are reported
Never attempt to change the value attribute when using the
HtmlHelpers (or the name attribute). The helpers generate the
correct html based on the model property and changing them from the
default values will only lead to model binding failing

Model binding select option value

I use pure html since I do not like the #Html class.
How do I get the value from an option in a select input ?
The model binder gives me the text property of an option and not the value, as I would have liked :)
Do I really have to use Request.From[""] to do it ?
Thanks!
EDIT
This will make the text attribute, not the value attribute, appear in the viewmodel
public ActionResult UpdateCard( UpdateCreditCardViewModel form )...
And if I just add another parameter with the input name, then the viewmodel is correcly populated. I don't have to do anything else it seems.
public ActionResult UpdateCard( UpdateCreditCardViewModel form, string Country )...
I guess that's the default behaviour ?

HtmlHelper.TextBox uses the model value even when an explicit value provided

In a view I'm using this overload of HtmlHelper.TextBox:
public static MvcHtmlString TextBox(
this HtmlHelper htmlHelper,
string name,
Object value
)
The documentation states:
value
Type: System.Object
The value of the text input element. If this value is null, the value of the element is retrieved from the ViewDataDictionary object. If no value exists there, the value is retrieved from the ModelStateDictionary object.
I do provide a value when I call this overload, and this value is not null. Nevertheless, the value for the textbox is retrieved from the ModelStateDictionary whenever it is present there.
In order to force the textbox to use the value provided inline, I have to reset the model first in the controller (or remove the key with the textbox's name from the keys collection).
Same applies to other controls rendered by HtmlHelper.
Where is my understanding wrong? Or is that a bug in the documentation?
ModelState always has precedence in all input helpers, not just TextBox. This allows you to specify a default value in the view, and after POST render the posted value (taken from ModelState) without having to manually pass the posted value to the view. To use a different value you must remove the entry from ModelState.
This is how the model binder works, it takes the values from the modelstate, probably to preserve the values from a failed postback or validation failure. Have you tried using the Html attributes to set the value
Html.TextBox("Id", new { Value = Model.Id})
Si
Maybe this will help.
Using viewmodel with property below and ViewData["textboxvalue"]
public string Id { get; set; }
If you have provided an object that can be bound into the "value" parameter, the the html helper will always use its value.
-Text box value bound to view model - these show the model's id
#Html.TextBox("Id", Model.Id)
-Text box with provided value from view data - these show the view data's value
#Html.TextBox("Id", ViewData["textboxvalue"])
Even if the name provided is not the same as the property name, the value will still be that provided by the model
-Text box value bound to view model - these show the model's id
#Html.TextBox("Id2", Model.Id)
-Text box with provided value from view data - these show the view data's value
#Html.TextBox("Id2", ViewData["textboxvalue"])
If you provide your own custom
-Text box with provided hardcoded value - this shows the string provided in view
#Html.TextBox("Id", "my hardcoded value in view")
The documentation is stating - if you do not populate the "value" parameter, as in the below:
#Html.TextBox("Id", null)
Then mvc framework will automatically try and search both the model that is bound to the view and the data dictionary.
If you have this case, then
-Found a property called "Id" in view model. Text box value set to the model's value
#Html.TextBox("Id", null)
But if the "name" value is not a property/key in the model/dictionaries, then the framework could not find any value, so the text box value will be an empty string
-Text box with name that is not property in view model or view data
#Html.TextBox("Id2", null)

Categories

Resources