Here's the code:
public class MessagesController
{
public virtual ActionResult Compose(ComposeMessageViewModel composeMessageViewModel = null)
{
if (composeMessageViewModel == null)
{
// never executed as composeMessageViewModel is always not null
composeMessageViewModel = new ComposeMessageViewModel();
}
return View(composeMessageViewModel);
}
}
And the definition of ComposeMessageViewModel
public class ComposeMessageViewModel
{
[DisplayName("To:")]
[NotEmpty] //custom ValidationAttribute
public IEnumerable<MessageRecipientViewModel> Recipients { get; set; }
[DisplayName("Subject:")]
public string Subject { get; set; }
public string Body { get; set; }
}
The problem is, when I navigate to /Messages/Compose (no query string, no form parameters), I'm expecting the parameter to be null so that no validation errors would occur, but it's an actual object with all its fields/properties set to default values.
This is undesirable as it causes the validation for the model to be executed, when it should not be as nothing has been entered yet!
There's no custom ModelBinder set for this class, and the default ModelBinder has not been changed.
WTF?
Isn't that what your code is doing - creating an object with default values?
if (composeMessageViewModel == null)
{
composeMessageViewModel = new ComposeMessageViewModel();
}
The true answer: PEBKAC. I originally had a Send action which, if validation failed, I thought I would have to redirect to the Compose action for some reason instead of just returning the appropriate view with the appropriate ViewModel. Duuuuuuuh. :)
Related
I got this problem when I receive values from a class that use ExecuteAsync.
For example check tempPrice, this nullable var continue to change every second fraction and it's never zero. Other va are props and can be true, false or null... I always get null.
The following is the action method of a specified controller:
public IActionResult Index()
{
ViewBag.tempPrice = _Strategy.TempPrice.ToString().Replace(".", ",");
ViewBag.TradingPair = _Strategy.Symbol;
ViewBag.StopOrderPrice = _Order.StopPriceSpotOrder.ToString().Replace(".", ",");
ViewBag.CurrentTradePrice = _Trade.LastPublicTrade.ToString().Replace(".", ",");
return View();
}
These are parts of codes where i get values , just for example...
public class OrdersFunctions
{
public string ClientOrderId { get; set; }
public string Id { get; set; }
public string Symbol { get; set; }
public string Side { get; set; }
public string Status { get; set; }
}
Only where I manually set the value during app testing (for example)
public string Symbol { get; set; } = "ETHBTC";
I can get the correct value.
As you can see in the screenshot, every value is set to zero, or null, but in the class that contain vars, are all valued.
I supposed it was a problem of services Singleton, Transient, etc... but in any case I set them, I always have this problem (with or without interfaces).
Can someone help me?
EDIT :
As results from the following pic, you can see that all my values, during task operation, are OK. ONLY when i pass the values by contructor in this controller, all values go zero to or null.
The pic show that _trade var has Side value filled in main logic class, but it pass a null value to Controller class
I have used this contructor, but nothing... always zero...
public StrategyController(IOptions<StrategyManager> sm, IOptions<TradeFunctions> trade, IOptions<OrdersFunctions> order )
{
_Strategy = sm.Value;
_Order = order.Value;
_Trade = trade.Value;
}
I have two controllers(controllerA.actionA & controllerB.actionB) which share the same view & ViewModel(ViewModel_Shared).
So, in both Controller, I use RedirectToAction to redirect to a third controller(controllerC.actionC) which points to the shared view.
All three Actions are in different Controller.
When ActionA or ActionB was invoked All the parameters that post from view was sent to the modelC in actionC successfully.
However, when I try to feed data into the object(Item), NullReference exception was thrown. the object(Item) was NULL.
But I'm sure the constructor of ViewModel_Shared was hit TWICE while ActionA was called and ActionC was called.
So, basically, object(Item) was NEW twice.
I don't really understand why it's like that.
thank you all in advance!!!
public ActionResult actionA (ViewModel_Shared modelA)
{
return RedirectToAction("actionC", "controllerC", modelA);
}
public ActionResult actionB (ViewModel_Shared modelB)
{
return RedirectToAction("actionC", "controllerC", modelB);
}
public ActionResult actionC (ViewModel_Shared modelC)
{
modelC.FillEditedData();
// Send the data to view
ViewData.Model = modelC;
return View();
}
public class ViewModel_Shared
{
public ItemCustomer Item { get; set; }
public ViewModel_Shared()
{
Item = new ItemCustomer();
}
public void FillEditedData()
{
// NullReference exception was throw here, somehow, Item is null.
Item.LegalCost = "some value";
}
}
[Serializable]
public class ItemCustomer
{
public string Item_No { get; set; }
public string MaterialPricingGroup { get; set; }
public string MaterialGroup { get; set; }
public string Plant { get; set; }
public string LegalCost { get; set; }
}
As mentioned, complex objects cannot travel with requests.
Instead you need to serialize the object and pass the serialized data to the destination action method:
public ActionResult actionA (ViewModel_Shared modelA)
{
var serializedModel = JsonConvert.SerializeObject(modelA);
return RedirectToAction("actionC", "controllerC", serializedModel);
}
UPDATE
My bad. Indeed the parameters, sent through RedirectToAction() are sent as route values so even serialization cannot help here. At least not by itself - the routing must be setup appropriately ...
A different approach must be taken and during actionA, place your modelA into the TempData, then do redirect. In the actionC retrieve the model and carry on.
public ActionResult actionA (ViewModel_Shared modelA)
{
TempData["shared_model"] = modelA;
return RedirectToAction("actionC", "controllerC");
}
Later, in actionC:
public ActionResult actionC (ViewModel_Shared modelA)
{
var modelC = TempData["shared_model"];
modelC.FillEditedData();
ViewBag.Model = modelC;
return View();
}
Do note, that the objects in he TempData are alive only to the next request and then gone. Here you can read more about the TempData.
Yin
1) if you are using the ItemCustomer class as a property you should initialize it when you are using.
public ItemCustomer Item { get; set; }
2) if you are using ItemCustomer as object it will initialize when you constructor call as your example.
public ItemCustomer Item;
I have run the second point without get and set it's working fine.
I'm working on the business model for my first project(please excuse me if someone finds my code lack of quality, important thing is i'm making progress). I'm stuck trying to find the reason for a bug. I'm creating a view which rely on reflection of properties and custom attributes. I get a null reference exception when i use the PropertyInfo.GetCustomAttribute for the second time on a "property's property". Why does my second call return null. As you can see I have applied the attribute on the property(_TopSchools) which i invoke method on.
public class EducationFilter : Filter
{
[FilterAttribute(FilterType.Child, "Topschools")]//I cant get this attr!
public TopSchoolFilter _TopSchool { get; set; }
}
public class TopSchoolFilter :BooleanFilter
{
}
public class Filters
{
[FilterAttribute(FilterType.Parent, "Education")] //This i can...
public EducationFilter _EducationFilter { get; set; }
public Filters(EducationFilter educationFilter)
{
this._EducationFilter = educationFilter;
}
}
public StackLayout GenerateFilterView(PropertyInfo p,TestModel vm)
{
StackLayout tempStack = new StackLayout();
**FilterAttribute filterAttr = p.GetCustomAttribute<FilterAttribute>();**//This returns the attr instance
IEnumerable<PropertyInfo> filterProperties = p.PropertyType.GetRuntimeProperties();
foreach (PropertyInfo p1 in filterProperties)
{
**FilterAttribute filterAttr1 = p1.GetCustomAttribute<FilterAttribute>();**//But not this one, i get null
If GetCustomAttribute<T>() returns null then that means the custom attribute provider (the property in this case) doesn't have an attribute of that type. If you are only interested in properties with this attribute, you can just skip over the properties without the attribute.
if (filterAttr1 == null) {
continue;
}
I am posting a form in an asp.net-mvc page. Here is my controller action:
public ActionResult UpdateData(MyFormObject entity)
{
bool isValid = IsValid(entity);
if (!isValid)
{
var firstError = ModelState.Values.SelectMany(v => v.Errors).First();
throw new HttpException(404, firstError.ErrorMessage);
}
return Json(BuildResult(entity));
}
Even thought the post passes all of my explicit validation logic, when i check ModelState I see errors. I am seeing errors in ModelState when any of my properties are empty. Here is my object:
public class MyFormObject
{
public int Id{ get; set; }
public int TestId{ get; set; }
public int OtherId{ get; set; }
}
and I am looking at Model.State and i see errors for any element in my object that is not populated.
If I change this to (NOTE: the "?")
public class MyFormObject
{
public int? Id{ get; set; }
public int? TestId{ get; set; }
public int? OtherId{ get; set; }
}
then i no longer get any errors. Is there some default validation that is happening here that I am not setting. I am trying to figure out what is setting ModelState errors in the first case above.
When you are positing to a controller action that takes MyFormObject class object as a parameter, MVC engine will try to create an instance of that class via automatic model-binding. In order to create MyFormObject one needs to provide all of these:
public int Id{ get; set; }
public int TestId{ get; set; }
public int OtherId{ get; set; }
and if you don't provide at least any of these, it will try to assign null to the corresponding property. The int (value type) doesn't support null values, whereas int? does.
This is logically correct and actually helps you in a long run.
As #Maxim V. Pavlov said, when you post, ASP.MVC engine will try to validate the model, ie its class, and based on class you cited as example, the properties don't accepts a null or empty value, then it will throw an exception and ModelState will be invalid.
You can see more here # Validating Model Data in an MVC Application and here ModelStateDictionary.IsValid Property
You are probably submitting an empty MyFormObject to your UpdateData method. Value-types cannot be null and must be assigned a value. If a value is missing for a value-type, then it automatically will trigger a required field validation.
ASP.NET MVC even has a property that allows you to tweak this behaviour, although I believe it will only influence client-side validation:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes
I've been struggling for this for an hour and I don't understand why there's a problem.
I have an abstract class
public abstract class IValidated
{
public bool IsValid { get { return (GetRuleViolation() == null); } }
public abstract RuleViolation GetRuleViolation();
}
And a validation class
public class RegisterModel : IValidated
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public MyDataContext DB { get; set; }
// ....
}
When I use the validation, I get an error
public ActionResult Register(RegisterModel model)
{
model.DB = DB;
if (model.IsValid) // an exception here
}
DB is null! I need to pass the datacontext object to the validation to check if the e-mail is unique and stuff like that.
Here's how the DB is initialized:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
DB = new MyDataContext();
}
The DB property is set in the Register method, I debugged that. But in the IsValid method of the class it's null...
UPDATE
Looks like the MVC framework for some reason runs the IsValid method before the Register ActionResult (and before the DB object is initialized). I think it's because it's doing all "magic" stuff with passing the RegisterModel parameters to the view. So I just put
if (DB != null)
and it helped.
Apparently, the IsValid method is run again when I call it, and the DB object is not null by then.
UPDATE 2
Since IsValid was a property, ASP.NET MVC was binding it, after turning it into a method, the problem disappeared.
You may want to rename IValidated to ValidationBase or something - the I prefix denotes an interface rather than a base class.
Is DB being set up in your controller's constructor? I'm guessing it's not, which leads to the Register method assigning a null reference to model.DB which leads to your NullReferenceException.
Also, MVC2 will read properties during databinding to try to validate them. That's probably what's biting you. If you change IsValid from a property to a method, the problem will go away.
Where are you creating the instance variable DB?
model.DB = DB
Where is the right DB getting initialized?