3

Let me start telling the problem: When I submit my View, my Action receives a object from a class called Customer that have foreign keys that are integer but should be nullable.

The class was generated by EF Designer, and the structure of my database is the following (I wish put an image, but, it tells me that I need 10 reputation):

Customer (Table)
  CityId -> City (Table)
            CityId
            StateId -> State (Table)
                       StateId
                       CountryId -> Country (Table)
                                    CountryId

Resuming, I have a Customer that must be in a city, that must be related with a state, that must be related with a Country.

In other hand, I have my View that has 3 Dropdowns: 1 to choose the country, another to choose one state from the selected country, and the third one that is to choose the City from the selected state. When I select the Country, the View (via JSON), fill another dropdown with the states, etc, so, it's obviously that I save only the CityId.

The problem is, when I try submit, the MVC shows me the following validation:

The CountryId field is required.
The StateId field is required.
The CityId field is required.

It occurs because those fields are Int32. So, the first thing that I did was change, in the EF Designer those fields to nullable (because I want to put a personal validation via ModelState.AddModelError).

I also changed the Multiplicity to 0..1.

In the database, those fields must to be non nullable.

But now, I am getting the following error: Error 3 Error 3031: Problem in mapping fragments starting at line 1074:Non-nullable column Cidades.EstId in table Cidades is mapped to a nullable entity property.

What is the best way to fix this?

Thanks in advance

3
  • Are you using your entity models in your view? You might want to consider composing view models and then mapping them back to the entity models before the update. Sounds like a hassle, but AutoMapper really helps there. lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models Commented May 6, 2015 at 16:35
  • Hi @SteveGreene! I am using entity models in my View. In other big project, I used to compose view models, but, it is a hard work, redundant and harder to manage (when the project is really big). This project is going to be big to, and I don't want the have the same problem of maintenance of "temporary" classes. I suppose that setting the field to int? (instead only int) is the best way, but, I never did it before using EntityFramework. Commented May 6, 2015 at 16:50
  • I would argue that when the project is really big you especially need to follow the ViewModel pattern. It separates your concerns and makes future maintenance much easier. For your current problem you're going to end up making a nullable field when (it sounds like) the business rule requires it. Automapper is your friend here. Commented May 6, 2015 at 16:57

2 Answers 2

1

The problem is easy to solve: design the view so that the posted form only contains the properties of the entity that you're trying to store in the database (the Customer).

What that means is that:

  • you have to use the Html Helpers only for creating controls related to the Customer properties and not to other entities properties. I.e. don't use the Html Helpers to create controls for "Customer.City.CityId", but for "Customer.CityId". In this way you assure that only the Customer properties are posted to your controller action.
  • (I'm rpeating myself, but) You have to avoid creating controls related to any other of the related entities: City, State and Country. This controls should be plain HTML objects. You only have to access to them later by using JavaScript (for example, to load the cascaded lists). The only control which must be related to any property is the Drop Down List (<select>) which contains the city, which must have the Customer.CityId name, and not the Customer.City.CityId.

If you do so, when you post your form, you're posting only the Customer object, thus the post action in your controller should only receive a Customer parameter. Then EF will not trigeer any validations for any of the other entities, simply becasue they don't exist and you'll get rid of the problem.

About the validation: depending on your configuration the validation can happen both in the client and on the server. If you take this precautions you'll avoid the problem in both sides. Don't change your model to accept nulls! That's a bad hack!

NOTE: as you didn't show your code, I'm guessing how it more or less look like. If my answer it's not clear enough, please, update your question, and let me know wit a comment with @JotaBe

Sign up to request clarification or add additional context in comments.

3 Comments

Hi @JotaBe, your think is right. I could do that, and, in some way you are right, but, is this the best pratice?
Excuse me, I broke the original comment by mistake. Yes, it's a good practice to have separate classes for creating the view (view model) and for receiveng the data submitted in a POST action. And it's good practice to send to the server only the required information, and no more. Perhaps you've seen simple cases and tutorials that use the same class for everything. Even VS scaffolding does it. That's really good for simple cases (fast!) but when the thing that you POST and the thing that you receive are so different, it's better to do it this way
You only want to manage an EF disconnected tree if you want to update related info on a single batch (for example a shopping cartm which includes the details is a good sample). But, in this case, if you're editing the Customer you don't need all the related info in "parent tables" which you can reach from your Customer. They're already in the database.You don't need to modify them. Don't send them back to the server to create confussion
0

I fixed that removing the primary validation imposed by MVC:

ModelState.Remove("City.State.CountryId");
ModelState.Remove("City.StateId");
ModelState.Remove("CityId");

But, the tip that was given by @JotaBe is a interesting solution too.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.