MVC and NOSQL: Saving models directly in MongoDB?

I understand that the β€œright” structure for separating problems in MVC is to have view models to structure your views and separate data models to store in your chosen repository. I started experimenting with MongoDB, and I'm starting to think that this might not apply when using a style database without NO-SQL. I wanted to introduce this script to the stackoverflow community and see what all thoughts are. I'm new to MVC, so that made sense to me, but maybe I'm missing something ...

Here is my example for this discussion: when the user wants to edit their profile, they will go to the UserEdit view, which uses the UserEdit model below.

public class UserEditModel { public string Username { get { return Info.Username; } set { Info.Username = value; } } [Required] [MembershipPassword] [DataType(DataType.Password)] public string Password { get; set; } [DataType(DataType.Password)] [DisplayName("Confirm Password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } [Required] [Email] public string Email { get; set; } public UserInfo Info { get; set; } public Dictionary<string, bool> Roles { get; set; } } public class UserInfo : IRepoData { [ScaffoldColumn(false)] public Guid _id { get; set; } [ScaffoldColumn(false)] public DateTime Timestamp { get; set; } [Required] [DisplayName("Username")] [ScaffoldColumn(false)] public string Username { get; set; } [Required] [DisplayName("First Name")] public string FirstName { get; set; } [Required] [DisplayName("Last Name")] public string LastName { get; set; } [ScaffoldColumn(false)] public string Theme { get; set; } [ScaffoldColumn(false)] public bool IsADUser { get; set; } } 

Note that the UserEditModel class contains an instance of UserInfo that inherits from IRepoData? UserInfo is what is stored in the database. I have a generic repository class that accepts any object that inherits the IRepoData form and saves it; so I just call Repository.Save(myUserInfo) and Repository.Save(myUserInfo) it. IRepoData defines _id (the MongoDB naming convention) and timestamp, so the repository can be updated based on _id and check for conflicts based on the timestamp and any other properties that the object just saved in MongoDB. By and large, we just need to use @Html.EditorFor , and we are good to go! In principle, everything that is required only for presentation is included in the base model, everything that is required only by the repository simply receives the [ScaffoldColumn(false)] annotation, and everything else is common to these two. (BTW - username, password, roles, and email are stored in .NET providers, so they are not in the UserInfo object.)

the big advantages of this scenario are twice ...

  • I can use less code , which, therefore, is more easily understood, develops faster and becomes more convenient (in my opinion).

  • I can change the ratio in seconds ... If I need to add a second email address, I just add it to the UserInfo object - it will be added to the view and saved to the repository, just adding one property to the object. Since I'm using MongoDB, I don't need to modify the db schema or mess with any existing data.

Given this setting, do I need to create separate models for storing data? What do you all think about the disadvantages of this approach? I understand that the obvious answers are standards and separation of concerns, but are there any examples in the real world that you can think about to demonstrate some of the headaches that this would cause?

It's also worth noting that I'm working on a team of two developers, so it's easy to look at the benefits and ignore the bend of some standards. Do you think working on a small team matters in this regard?

+7
source share
2 answers

The advantages of view models in MVC exist regardless of the database system used (damn, even if you don't use it). In simple CRUD situations, your business model objects will very accurately mimic what you show in the views, but in something more than a basic CRUD, it will not.

One of the important things is the problem of business logic and data integrity using the same class for modeling and storing data that you use in views. Take a situation where you have the DateTime DateAdded property in your custom class to indicate when the user was added. If you provided a form that goes directly to your UserInfo class, you will get an action handler that looks like this:

 [HttpPost] public ActionResult Edit(UserInfo model) { } 

Most likely, you do not want the user to be able to change when they were added to the system, so your first thought is not to provide a field in the form.

However, you cannot rely on this for two reasons. Firstly, the value for DateAdded will be the same as you if you made new DateTime() or it will be null (any of these methods will be wrong for this user).

The second problem is that users can spoof this in the form request and add &DateAdded=<whatever date> to the POST data, and now your application will change the DateAdded field in the database to whatever the user entered.

This is by design because the MVC model binding mechanism looks at the data sent through POST and automatically tries to associate it with any available properties in the model. He has no way of knowing that the property passed was not in its original form and, therefore, it will still bind it to this property.

ViewModels does not have this problem, because your view model needs to know how to convert itself to / from a data object, and it does not have a DateAdded field to replace it, it only has the minimum fields that it needs to display (or receive) data.

In your specific scenario, I can easily reproduce this by manipulating the POST string, since your view model has direct access to your database.

Another problem with using data classes directly in views is when you try to present your view in a way that is not suitable for modeling your data. As an example, suppose you have the following fields for users:

 public DateTime? BannedDate { get; set; } public DateTime? ActivationDate { get; set; } // Date the account was activated via email link 

Now let me say that the administrator is interested in the status of all users, and you want to display a status message next to each user, and also give different actions that the administrator can perform based on this user status. If you use your data model, your code will look like this:

 // In status column of the web page data grid @if (user.BannedDate != null) { <span class="banned">Banned</span> } else if (user.ActivationDate != null) { <span class="Activated">Activated</span> } //.... Do some html to finish other columns in the table // In the Actions column of the web page data grid @if (user.BannedDate != null) { // .. Add buttons for banned users } else if (user.ActivationDate != null) { // .. Add buttons for activated users } 

This is bad, because now you have a lot of business logic (user status is always denied over activated users, banned users are defined by users with a banned date, etc.). It is also much more complicated.

Instead, the best (at least) solution is to wrap your users in a ViewModel that has an enumeration for their status, and when you convert your model to your view model (the view model constructor is a good place to do this ) you can insert your business logic once to see all the dates and find out what status the user should be.

Then your code above is simplified as:

 // In status column of the web page data grid @if (user.Status == UserStatuses.Banned) { <span class="banned">Banned</span> } else if (user.Status == UserStatuses.Activated) { <span class="Activated">Activated</span> } //.... Do some html to finish other columns in the table // In the Actions column of the web page data grid @if (user.Status == UserStatuses.Banned) { // .. Add buttons for banned users } else if (user.Status == UserStatuses.Activated) { // .. Add buttons for activated users } 

There may be less code in this simple scenario, but it makes things much more convenient when the logic for determining user status becomes more complex. Now you can change the logic of determining the status of a user without changing the data model (you do not need to change the data model due to the way you view the data), and it saves the state definition in one place.

+9
source

TL; DR

The application has at least 3 layers of models, sometimes they can be combined safely, sometimes not. In the context of the issue, it is good to combine persistence and domain models, but not a representation model.

full post

The scenario that you describe is equally well suited for any entity model. It can use the Linq2Sql model as a ViewModel, an entity framework model, a sleep model, etc. The main thing is that you want to use the saved model directly as a model of your view. Separating problems, as you say, clearly does not force you to avoid this. In fact, sharing problems is not even the most important factor in building your models.

In a typical web application, there are at least 3 different layers of models, although it is sometimes possible to combine these layers correctly in one object. The levels of the model are from the highest to the lowest, a model of your kind, a model of your domain, and a persistence model. Your review model should accurately describe what you think is no more and no less. Your domain model should accurately describe your complete system model. Your persistence model should accurately describe your storage method for your domain models.

ORM come in many shapes and sizes, with different conceptual goals, and MongoDB, as you describe, is just one of them. The illusion that most of them promise is that your persistence model should be the same as your domain model, and ORM is just a tool to map from your data store to a domain object. This is certainly true for simple scenarios where all your data comes from one place, but ultimately has its own limitations, and your storage degrades into something more pragmatic for your situation. When this happens, the patterns tend to become different.

One good rule to keep in mind when deciding whether you can separate your domain model from your persistence model is whether it is easy to replace the data store without changing the domain model. If the answer is yes, they can be combined, otherwise they should be separate models. The repository interface is naturally suitable for delivering your domain models from any data warehouse. Some of the new lightweight ORMs, such as dapper and massive , make it very easy to use your domain model as a persistence model because you don't need a specific data model to persist, you just write queries directly and let ORM just handle the mapping.

On the reading side, view models again constitute a separate model layer, since they are a subset of your domain model combined, however you need to display the information on the page. If you want to display information about the user, with links to all your friends, and when you put your name on them, you get some information about this user, your persistence model, which will handle this directly, even with MongoDB, will most likely be crazy. Of course, not every application displays such a set of interconnected data on each view, and sometimes the domain model is exactly what you want to display. In this case, there is no reason to put overweight matching from an object that has exactly what you want to display for a particular view model that has the same properties. In simple applications, if all I want to do is increase the domain model, my view model will directly inherit from the domain model and add additional properties that I want to display. However, before your MVC application becomes large, I highly recommend using a view model for your layouts and have all page-based model models inherited from that layout model.

On the recording side, the view model should only allow the properties that you want to edit for the type of user access to the view. Do not submit an administrator view model to a non-user view. You can get away from this if you write the mapping layer for this model yourself in order to take into account user privileges, but this is probably more overhead than just creating a second administrator model, which inherits from the usual view model and complements it with administrator rights.

Finally about your points:

  • Less code is only an advantage when it is actually more understandable. Readability and the ability to understand it are the results of the skills of the person who writes it. Examples of short code are known, which made even strong developers understand and understand for a long time. Most of these examples are taken from cunningly written code that is no more understandable. More importantly, your code matches your specification 100%. If your code is short, easy to understand and read, but does not meet the specification, it is useless. If all this meets the specification, but can be easily used, the specification and code is useless.

  • Refactoring in seconds is safe - the result of well-written code, not patience. Following the DRY principle, your code will be easily reconstructed if your specification meets your goals correctly. In the case of model layers, your domain model is the key to writing good, convenient, and easy to refactor code. Your domain model will change at the pace at which your business requirements will change. Changes in your business requirements are big changes, and you need to make sure that the new specification is fully thought out, designed, implemented, tested, etc. For example, today you say you want to add a second email address. You still have to change the look (if you are not using any kind of forest). Also, what if you receive a change in requirements tomorrow to add support for up to 100 email addresses? Initially, the proposed change was quite simple for any system; large changes require more work.

+7
source

All Articles