mvc model with foreign key relationship - asp.net-mvc

I am having data annotation validation issue with model containing model of foreign object.
Lets say
Class Foo
{
public virtual Int Id {get; set;}
[Required]
public virtual String Name {get; set;}
}
Class Bar
{
public virtual Int Id {get; set;}
[Required]
public virtual String AnotherName {get; set;}
public virtual Foo foo {get; set;}
}
FOO is NOT mandatory/required while creating BAR.
But when i try to check the ModelState in HTTPPOST of BAR it say model state is invalid, and report the column of FOO as missing.
[HTTPPOST]
public ActionResult SaveBar(BAR bar)
{
if (ModelState.IsValid)
SaveBar(bar);
}
I have set FOO as null, as tried TryUpdateModel, but still didnt help.
EDIT
I am dealing with entities and not view models
And also.. How to tell model binder not to check the FOO when binding BAR...
EDIT
modified example..

It appears that the model binder is instantiating Foo as a member of the Bar object when you hit 'SaveBar()'. Thus, the attribute validation on Foo fires, just as you would expect.
Here's what I would do: remove the attribute validation and just go a different route, maybe like a custom validation method that you call from the controller actions for normal "Foo" operations. "SaveBar()" can check Foo for a default state and decide to call that validation or not, depending.

It doesn't work the way you think it is. The ModelState is invalid because you declared the fields of Foo as required. If your view does not accept and pass values for Foo.Id and Foo.Name then you will get an invalid ModelState.

Don't use [Required] on your model's id. I'm not sure whether you're dealing with entities or view models here, but in general, the id should be allowed to be unset. In the case of entities, the id will not be set until it is saved to the database (typically) and in the case of a view model, you might be representing an entity that has not previously been saved (and thus has no id).
[Required] has only two real uses, so you should understand exactly what those are and then only apply the attribute accordingly:
1) On an entity, [Required] will imply NOT NULL. It really only makes sense on strings since every other scalar type is NOT NULL by default.
2) On a view model being used to accept form data from a POST. This will require that the field not be blank. If your id is not a form field being presented to the user for input (which I highly doubt it is), then it should not be required.

#hgrathi, what you are not understanding is that as soon as you made Name in Foo Required, Foo is now Required under Bar.
One way to solve this is to create a custom model binder by implementing IModelBinder.

Related

Can an ASP.NET MVC model class have a property called Model?

I've just spent quite some time tracking down a defect where a JSON model parameter to an MVC action method was always null. The cause is different to those raised in other related questions, hence the new question.
My action looks something like this:
[HttpPost]
public ActionResult SendDeviceDetails(DeviceModel model)
{
model.DoStuffHere();
}
And my model class looks like this:
public class DeviceModel
{
public string Manufacturer { get; set; } // e.g. "Asus"
public string Model { get; set; } // e.g. "Nexus 7"
// etc.
}
The model object is posted as a JSON string from a mobile device.
However, the controller action always throws a NullReferenceException because model is always null. I spent some time verifying that the data is sent correctly from the client end before starting to strip down my model class and build it back up again. (In reality it has many more properties than I've shown here.)
What I found was this: if the model class has a property called Model, you will always get a null reference passed to your action method. If I rename that property (e.g. to ModelName), everything works perfectly.
Is this really as insane as it seems to me? Am I missing some good reason for this restriction or is it just a flat-out defect in MVC? And is there any way to work around it if I really want to have a property named Model?
The problem was caused by the property name (DeviceModel.Model) matching the action parameter name (model). Renaming either of them solves the problem. So actually it's OK to have a Model property in your model class, as long as the parameter you pass it as is not called model.
Crazy!

MVC 3 Model Binding without Key

I have a question about ASP.NET MVC3 model binding. If I have a class I'm trying to use as a model, but I don't want the key put on the page, the model doesn't bind on a POST. Here is an example:
//Data Model
public class MyModel
{
[Key]
public string MyKey {get;set;} //Perhaps this is an ssn that I don't want on the form.
public string MyValueToGet {get;set;} //This is the value I want the user to enter.
}
//Conroller code.
public ViewResult Index()
{
MyModel model = new MyModel{ MyKey = "SecretInfo", MyValueToGet = "" };
return View(new model);
}
public ActionResult Edit(MyModel model)
{
repository.SaveChanges(model)
}
//View code.
#using(Html.BeginForm("Edit", "Home", FormMethod.Post))
{
Enter a value: #Html.EditorFor(m => m.MyValueToGet)
<input type="submit" value="Salve" />
}
So my problem is that model is null when the Edit method is called upon form submission. I can fix this by putting MyKey somewhere on the page (perhaps as a hidden field), but that is unacceptable if it is some sort of sensitive data. Is there a way to solve this problem? I am new to MVC, so I appreciate any help.
Create another unique but otherwise meaningless identifier like an (auto increment int) and use that to bind.
in other words modify your model to something like:
public class MyModel
{
[Key]
public int ID {get; set;}
public string MyKey {get;set;} //Now this can be sensitive, it doesn't matter because you no longer rely on it.
public string MyValueToGet {get;set;} //This is the value I want the user to enter.
}
EDIT
I believe your best choice would be to change the MyModel object, as it's design is flawed. The primary key in the majority of cases (and I think this is one of them) should be a simple auto incrementing integer, meaningless apart from it's role as the table's key.
While Luke's suggestion to use Session is a viable option and a solution that would work, I would personally do something similar to what I'll explain here, as it would seem to me to be more of the 'mvc way' of doing things.
Data model:
Either change your current model to something like what I suggest above, or, if that is not feasible for whatever reason (breaking dependancies or FK relationships), create a new table that can be used as a join, or proxy, if you will:
public class Proxy
{
public int ProxyId {get;set;}
public MyModel MyModel {get; set;}
}
Obviously, you'd have to do some work to populate this table, but you would then be able to use it to fetch records from MyModel without accessing the MyKey property directly.
It's not considered good practice to use your data models directly in your views, so you want to create a view model as well
public class MyModelViewModel
{
public int ModelId {get; set;}
public string ModelValueToGet {get; set;}
}
Notice we don't even need the key containing sensitive data in the view model.
Then type your view to the viewModel, not the data model, and include a hidden field for the ModelId
#using(Html.BeginForm("Edit", "Home", FormMethod.Post))
{
Enter a value: #Html.EditorFor(m => m.ModelValueToGet)
#Html.HiddenFor(m => m.ModelId)
<input type="submit" value="Save" />
}
Now in your controller you have your get method
public ViewResult Index()
{
//fetch the users record from the database
//if you're using the Proxy table, you'll want to write a LINQ query here
//instantiate a viewModel and populate it's properties using the fetched record
//remember, the viewModel.ModelId should be set to MyModel.ID or Proxy.ProxyId
//render the view
}
And the post method
public ViewResult Edit (MyModelViewModel viewModel)
{
//fetch the users record from the database using viewModel.ModelId
//If you're using the proxy table, you'll need to use that LINQ query again here
//update the record you fetched with the new data the user just entered
//you have complete control here of what gets updated and what stays the same
//pass the updated record to the repository to save the changes.
//redirect the user to be on their merry way
}
I think that's about as well as I can lay it out. Hope it makes sense.
An alternative is to encrypt the id before sending it to the client. Check this post for more information on how to accomplish this.
Asp MVC 3: Modifiy Values Sent to View

Disable remote validation

I have a model that uses remote validation. The model also acts as a parent class to a child class. How would i disable remote validation in the view for a particular field?
Model code
public user
{
[Remote("Validateemail","User",etc)]
public string Email {get; set;}
}
public edituser:user
{
public int userid {get; set;}
public edituser(int userid,string email)
{
userid=userid;
Email=email;
}
}
My aim is to remove the remove validation in the edituser class in the view.
im assuming that you just need to display the email field and all the data in it as it is in editView because by defauflt a user cannot edit his/her email..
to so this you can just change the code in the view from
Html.EditorFor() to Html.DisplayFor()
this is the perfect case for viewmodel. create a different viewmodel for edit (as a matter-effect view should not be linked directly to domain models in most of the cases)
or you can use IValidatableObject or fluentvalidation , or you can use DataAnnotationsModelValidatorProvider for manually attaching attribute condition based if it applies in your case.

MVC post not populating the model

I cannot figure out why my model is not being populated. All the data posted is in the Request.Form, but the model is actually turning out to be null.
According to this answer on model with collection not populating on postback
In other words, (...) If any required fields are missing, or if the values are
submitted in such a way that they cannot be converted to the type of a required field, then the entire object will be left null
I have changed several value types, but I cannot get it to work.
Here is my model:
public class AddModel
{
//Get properties
public Vehicle vehicle;
//Post properties
[Required(ErrorMessage = "Please enter a start date")]
public DateTime StartDate;
public int? StatusCode;
public SelectList StatusCodes()
{
...
}
}
Can you think of why it's not being populated?
Making AddModel members Properties - adding get; set; to fields should solve your problem
As per #archil's response, you should make your public variables properties. Although this may work with types you will run into problems as soon as you add complexity. Certainly for classes but possible for nullable types too.
The model binders use reflection to parse the form fields into the model, and reflection works differently on properties to public variables - in the case of these models, the differences will likely be causing the failure here.
Hope that helps - and hat tip to #archil for answering this (probably) correctly, much sooner than me!

Where do you put your validation in asp.net mvc 3?

One common recommended practice in asp.net mvc is that you should not send your business models to your views.. instead you should create viewmodels specific to each view.
When that is done and you call the ModelState.IsValid method in your controller you are effectively checking the validity of the viewmodel but not the business object.
What is the conventional approach to dealing with this?
public class Person
{
public int ID {get; set;};
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
public virtual ICollection<Exam> Exams {get; set;}
}
public class PersonFormViewModel
{
public int ID {get; set;};
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
}
This is exactly what I have right now but Im not sure if the [Required] attribute should appear on both models or just the ViewModel or just the Business Model.
Any tips on this issue are appreciatedd.
More links to support my claim that it is a common good practice to always use view models.
How to add validation to my POCO(template) classes
http://blogs.msdn.com/b/simonince/archive/2010/01/26/view-models-in-asp-net-mvc.aspx
My preference is to do input validation on the view models, and business validation on the domain models.
In other words, any data annotations such as required fields, length validation, regex, etc should be done on your view models, and added to the model state when error occurs.
And you'll probably have business/domain rules that rely on more than just a "form", so you should do that either in the domain models (execute the validation after they're mapped back), or with a service layer.
All our models have a method called "Validate", which we call in the services prior to persisting. They throw custom exceptions if they fail business validation, which gets caught by the controller and also added to the model state.
May not be everyone's cup of tea, but it's consistent.
Example of business validation, as requested:
Here's an example of a domain model we have, which represents a generic "Post" (question, photo, video, etc):
public abstract class Post
{
// .. fields, properties, domain logic, etc
public void Validate()
{
if (!this.GeospatialIdentity.IsValidForThisTypeOfPost())
throw new DomainException(this, BusinessException.PostNotValidForThisSpatial.);
}
}
You see there, I am checking against business rules, and throwing custom exceptions. DomainException is our base, and we have many derived implementations. We have an enum called BusinessException, which contains values for all our exceptions. We use extension methods on the enum to provide the resource-based error message.
This is not simply a field on the model I'm checking, e.g "All posts must have a subject", because although that is part of the domain, it's input validation first and foremost, and thus is handled via the data annotations on the view model.
Now, the controller:
[HttpPost]
public ActionResult Create(QuestionViewModel viewModel)
{
if (!ModelState.IsValid)
return View(viewModel);
try
{
// Map to ViewModel
var model = Mapper.Map<QuestionViewModel,Question>(viewModel);
// Save.
postService.Save(model); // generic Save method, constraint: "where TPost: Post, new()".
// Commit.
unitOfWork.Commit();
// P-R-G
return RedirectToAction("Index", new { id = model.PostId });
}
catch (Exception exc)
{
var typedExc = exc as DomainException;
if (typedExc != null)
{
// Internationalised, user-friendly domain exception, so we can show
ModelState.AddModelError("Error", typedExc.BusinessError.ToDescription());
}
else
{
// Could be anything, e.g database exception - so show generic msg.
ModelState.AddModelError("Error", "Sorry, an error occured saving the Post. Support has been notified. Please try again later.");
}
}
return View(viewModel);
}
So, by the time we get to the "Save" method on the service, the model has passed input validation. Then the Save method calls post.Validate(), invoking business rules.
If an exception is raised, the controller catches it and displays the message. If it gets pass the Save method and another error occurs (90% of the time, it's Entity Framework, for example), we show a generic error message.
As I said, not for everyone, but this works well for our team. We have a clear separation of presentation and domain validation, and a consistent flow of control from the raw HTTP POST, to the redirect after success.
The MetaData "buddy" class is exactly what this is for. The validation is created once but can be used on both the model and the viewmodel classes:
public class PersonMetaData
{
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
}
[MetadataType(typeof(PersonMetaData))]
public class Person
{
public string Name {get; set;}
public string LastName {get; set;}
}
[MetadataType(typeof(PersonMetaData))]
public class PersonFormViewModel
{
public string Name {get; set;}
public string LastName {get; set;}
}
Great answer by RPM1984, and nice code sample.
My view is still that using Variant 2 from the start is a pragmatic balance between productivity and structure, but you always have to be willing to move to Variant 3 in some cases, so I advocate a mix of the two approaches where logical. Patterns & Practices however recommend always doing Variant 3 as it is the ideal separation of concerns etc, and it avoids you using two approaches in the same solution which some people don't like and many customers I work with pick Variant 3 and run with that for all models.
I think the key is what RPM1984 said - reusing your business entities inside your View Models is useful for the sake of reusing the validation, but do bear in mind that often your business logic needs to do different validation too (e.g. checking the record doesn't already exist). If you use Variant 3 it empowers you to focus your View Model validation purely on the needs of your views (at the expense of a little extra effort) but you will always need some kind of business logic validation too.
Given that you always pass viewmodels to your view and your domain models are not exposed to the end user through views then i don't see any need for validating domain model itself. For example if you receive PersonViewModel from view through form post you will transform it to Person Model (perhaps through automapper etc.) only if PersonViewModel is valid itself. So i believe that input validations should stay with the view models because they are the one that are bound to input.
Typically your ViewModel will contain a reference to your Model - a ViewModel is only necessary if you need to display additional information in your view that isn't available in your Model, there is no need to replicate the data already present.

Resources