MVC - reference a model from another model - model-view-controller

I've this scenario in my application:
Controller1
.GetItems()
Model1
.GetItems()
Controller2
.GetCurrentUser()
Model2
.CurrentUser
In this scenario Controller1.GetItems() calls Model1.GetItems() method. Model1.GetItems() method needs to know (for example) what is the role of the current user to build the correct list of items, and it should get it from the Model2.CurrentUser property (that stores the cached information about the current user).
Is it a good practice to acces a model from another model?
Thanks,
Regards

You are going to run into some arguments about the best way to do this, but at the end of the day, you have two options. Either you can have the model pull the information it needs from the other model or you can have the controller pass the information needed.
Based upon what I have read, as long as the model does not have any controller logic or view logic you are good so there is nothing wrong with having the model know about other models. However, others have argued that having the controller pass the information that is needed makes the code a bit easier to document since you can see that the model requires information from somewhere else. At the end of the day though, I see both as being valid and which one you choose to use will likely come down to personal preference.
Design - Controller Provides Data
ModelOne
User GetCurrentUser()
ModelTwo
Items[] GetItems(User)
Snippet - Controller Proivdes Data
Controller {
function doWork() {
User user = ModelOne.GetCurrentUser();
Items[] items = ModelTwo.GetItems(user);
}
}
Design - Model Gets Data
ModelOne
User GetCurrentUser()
ModelTwo
Items[] GetItems()
Snippet - Model Gets Data
ModelTwo {
Items[] GetItems() {
User user = ModelOne.GetCurrentUser();
...
}
}
Controller {
function doWork() {
Items[] items = ModelTwo.GetItems();
}
}

If your second model is going to referenced often by your first, you could compile your model classes into a separate library?

I think you shouldn't have references between models, the right way is to use the controller for this link. So in your case you could pass the CurrentUser as a parameter in the GetItems method.

Related

How do I bypass the limitations of what MVC-CORE controllers can pass to the view?

From what I've read, I'm supposed to be using ViewModels to populate my views in MVC, rather than the model directly. This should allow me to pass not just the contents of the model, but also other information such as login state, etc. to the view instead of using ViewBag or ViewData. I've followed the tutorials and I've had both a model and a viewmodel successfully sent to the view. The original problem I had was that I needed a paginated view, which is simple to do when passing a model alone, but becomes difficult when passing a viewmodel.
With a model of
public class Instructor {
public string forename { get; set; }
public string surname { get; set; }
}
and a viewmodel of
public class InstructorVM {
public Instructor Instructors { get; set; }
public string LoggedIn { get; set; }
}
I can create a paginated list of the instructors using the pure model Instructor but I can't pass InstructorVM to the view and paginate it as there are other properties that aren't required in the pagination LoggedIn cause issues. If I pass InstructorVM.Instructors to the view, I get the pagination, but don't get the LoggedIn and as this is just the model, I may has well have passed that through directly.
An alternative that was suggested was to convert/expand the viewmodel into a list or somesuch which would produce an object like this that gets passed to the view
instructor.forename = "dave", instructor.surname = "smith", LoggedIn="Hello brian"
instructor.forename = "alan", instructor.surname = "jones", LoggedIn="Hello brian"
instructor.forename = "paul", instructor.surname = "barns", LoggedIn="Hello brian"
where the LoggedIn value is repeated in every row and then retrieved in the row using Model[0].LoggedIn
Obviously, this problem is caused because you can only pass one object back from a method, either Instructor, InstructorVM, List<InstructorVM>, etc.
I'm trying to find out the best option to give me pagination (on part of the returned object) from a viewmodel while not replicating everything else in the viewmodel.
One suggestion was to use a JavaScript framework like React/Angular to break up the page into a more MVVM way of doing things, the problem with that being that despite looking for suggestions and reading 1001 "Best JS framework" lists via Google, they all assume I have already learned all of the frameworks and can thus pick the most suitable one from the options available.
When all I want to do is show a string and a paginated list from a viewmodel on a view. At this point I don't care how, I don't care if I have to learn a JS framework or if I can do it just using MVC core, but can someone tell me how to do this thing I could do quite simply in ASP.NET? If it's "use a JS framework" which one?
Thanks
I'm not exactly sure what the difficulty is here, as pagination and using a view model aren't factors that play on one another. Pagination is all about selecting a subset of items from a data store, which happens entirely in your initial query. For example, whereas you might originally have done something like:
var widgets = db.Widgets.ToList();
Instead you would do something like:
var widgets = db.Widgets.Skip((pageNumber - 1) * itemsPerPage).Take(itemsPerPage).ToList();
Using a view model is just a layer on top of this, where you then just map the queried data, no matter what it is onto instances of your view model:
var widgetViewModels = widgets.Select(w => new WidgetViewModel
{
...
});
If you're using a library like PagedList or similar, this behavior may not be immediately obvious, since the default implementation depends on having access to the queryset (in order to do the skip/take logic for you). However, PagedList, for example has StaticPagedList which allows you to create an IPagedList instance with an existing dataset:
var pagedWidgets = new StaticPagedList<WidgetViewModel>(widgetViewModels, pageNumber, itemsPerPage, totalItems);
There, the only part you'd be missing is totalItems, which is going to require an additional count query on the unfiltered queryset.
If you're using a different library, there should be some sort of similar functionality available. You'll just need to confer with the documentation.

In the MVC programming pattern, which category does the following code fall under?

If I have some code which is will HTTP request a remote website and extract data from it, or login to this website and extract data from it; what would I put this under? The model or the controller? I'm think that it is a model, because the controller should talk to the user and call upon my models to do stuff.
Is this correct?
Model is correct. The data crunching portion should be left to the models. The controllers direct the traffic between the models and views. Finally, views are used to display the data.
You would probably do this a view or controller. If you will need to do any large manipulation with the data, you could create and action that makes a request and then manipulates the data and then pass that data onto a view. If you will need simpler operations, and depending on your framework, you can make the request in the view itself.
You would possibly use all of the View, Model and controller. But if you don't want to display the result, it would likely be just the controller and model.
For example, the controller code would have an ActionResult method which would call your remote website.
e.g. your model
public class RemoteWSData
{
public string Code { get; set; }
public string Name { get; set; }
}
your controller:
public ActionResult GetRemoteWebsiteData()
{
RemoteWSData model = repository.GetRemoteWebsiteData();
string code = model.Code;
string name = model.Name;
}
with the repository above being a data access class. This data access class would contain references to the connections required to access the data or server and get these values from a web.config file.

Model binding in controller when form is posted - why to use view model instead of class from domain model?

I'm still reasonably new to ASP.NET MVC 3. I have come across view models and their use for passing data from a controller to the view. In my recent question on model binding two experts suggested that I should use view models for model binding as well.
This is something I haven't come across before. But both guys have assured me that it is best practise. Could someone maybe shed some light on the reasons why view models are more suitable for model binding?
Here is an example situation: I have a simple class in my domain model.
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
And this is my controller:
public class TestController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult ListParents()
{
return View(testParentRepository.TestParents);
}
public ViewResult EditParent(int testParentID)
{
return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
}
[HttpPost]
public ActionResult EditParent(TestParent testParent)
{
if (ModelState.IsValid)
{
testParentRepository.SaveTestParent(testParent);
TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",
testParent.Name,
testParent.TestParentID);
return RedirectToAction("ListParents");
}
// something wrong with the data values
return View(testParent);
}
}
So in the third action method which gets invoked when an HTTP POST arrives I used TestParent for model binding. This felt quite convenient because the browser page that generates the HTTP POST request contains input fields for all properties of TestParent. And I actually thought that's the way the templates that Visual Studio provides for CRUD operations work as well.
However the recommendation that I got was that the signature of the third action method should read public ActionResult EditParent(TestParentViewModel viewModel).
It sounds appealing at first, but as your models and view actions get increasingly complex, you start to see the value of using ViewModels for (most) everything, especially input scenarios.
Case 1 - Most web frameworks are susceptible to over-posting. If you are binding straight to your domain model, it is very possible to over-post data and maliciously change something not belonging to the user. I find it cleaner to bind to an input view model than have long string lists of white lists or black lists, although there are some other interesting ways with binding to an interface.
Case 2 - As your input grows in complexity, you'll run into times when you need to submit and validate fields not directly in the domain model ('I Agree' checkboxes, etc)
Case 3 - More of a personal thing, but I find model binding to relational domain objects to be a giant pain at times. Easier to link them up in AutoMapper than deal with MVC's modelbinder for complicated object graphs. MVC's html helpers also work more smoothly against primitive types than deep relational models.
The negatives of using ViewModels is that it isn't very DRY.
So the moral of the story is, binding to domain models can be a viable solution for simple things, but as the complexity increases, it becomes easier to have a separate view model and then map between the two.

How do you exclude properties from binding when calling UpdateModel()?

I have a view model sent to the edit action of my controller. The ViewModel contains references to EntityObjects. (yea i'm fine with it and don't need to want to duplicate all the entities properties in the viewmodel).
I instantiate the view model and then call UpdateModel. I get an error that a property is "null" which is fine since it is a related model. I am trying to exclude the property from being bound during model binding. On debugging it I see in the entity where the model binder is trying to set the value of the property to null.
Here is my edit action:
var model = new SimplifiedCompanyViewModel(id);
var excludeProperties = new string[] {
"Entity.RetainedEarningsAccount.AccountNo"
,"Property.DiscountEarnedAccount.ExpenseCodeValue"
,"Entity.EntityAlternate.EntityID"
,"Property.BankAccount.BankAccountID"
,"Entity.PLSummaryAccount.AccountNo"
,"Property.RefundBank.BankAccountID"
,"Company.Transmitter.TCC"
};
try
{
UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
if (ModelState.IsValid)
{
//db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View(model);
}
I have looked at a few other issues about specifying a "prefix" but I don't think that is the issue since I am telling it to bind to the viewmodel instance not just the entity object.
Am I excluding the properties correctly? Strange thing is is only seems to happen on this item. I suspect it may be an issue with the fact that there is actually no refund bank related to my entity. But I have other related items that don't exist and don't see the same issue.
More info... since I'm told me model isn't designed well.
The Company is related to a BankAccount. The Company view shows the currently related BankAccount.BankAccountId and there is a hidden field with the BankAccount.Key. I use jQueryUI autocomplete feature to provide a dropdown of bank account displaying the BankAccount.BankAccountId and when one is selected the jQuery code changes the hidden field to have the correct Key value. So, when this is posted I don't want the current bankaccounts BankAccountID modified, hence I want it to skip binding that field.
If I exclude BankAccountId in the model then on the BankAccount edit view the user would never be able to change the BankAccountId since it won't be bound. I'm not sure how this indicates a poor model design.
Use the Exclude property of the Bind attribute:
[Bind(Exclude="Id,SomeOtherProperty")]
public class SimplifiedCompanyViewModel
{
public int Id { get; set; }
// ...
}
This is part of the System.Web.Mvc namespace. It takes a comma-separated list of property names to exclude when binding.
Also you should consider using TryUpdateModel instead of UpdateModel. You can also just have the default model binder figure it out by passing it as an argument to the constructor:
public ActionResult Create([Bind(Exclude="Id")]SimplifiedCompanyViewModel model)
{
// ...
}
A very simple solution that I figured out.
try
{
UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
ModelState.Remove("Entity.RetainedEarningsAccount.AccountNo");
ModelState.Remove("Property.DiscountEarnedAccount.ExpenseCodeValue");
ModelState.Remove("Entity.EntityAlternate.EntityID");
ModelState.Remove("Property.BankAccount.BankAccountID");
ModelState.Remove("Entity.PLSummaryAccount.AccountNo");
ModelState.Remove("Property.RefundBank.BankAccountID");
ModelState.Remove("ompany.Transmitter.TCC");
if (ModelState.IsValid)
{
//db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View(model);
}
Another option here is simply don't include this attribute in your view and it won't be bound. Yes - you are still open to model injection then if someone creates it on the page but it is another alternative. The default templates in MVC will create your EditorFor, etc as separate items so you can just remove them. This prevents you from using a single line view editor with EditorForModel, but the templates don't generate it that way for you anyways.
EDIT (adding above comment)
DRY generally applies to logic, not to view models. One view = one view model. Use automapper to easily map between them. Jimmy Bogard has a great attribute for this that makes it almost automatic - ie you create the view model, load up your Customer entity for example, and return it in the action method. The AutpMap attribute will then convert it to a ViewModel. See lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models
Try the Exclude attribute.
I admit that I haven't ever used it.
[Exclude]
public Entity Name {get; set;}

Can I include a convenience query in a Doctrine 2 Entity Method?

I'd like to include some additional functions in my Doctrine 2 entities to contain code that I'm going to have to run quite frequently. For example:
User - has many Posts
Post - has a single user
I already have a function $user->getPosts(), but this returns all of my posts. I'm looking to write a $user->getActivePosts(), which would be like:
$user->getPosts()->where('active = true') //if this were possible
or:
$em->getRepository('Posts')->findBy(array('user'=>$user,'active'=>true)) //if this were more convenient
As far as I can tell, there's no way to get back to the entity manager though the Entity itself, so my only option would be
class User {
function getActivePosts() {
$all_posts = $this->getPosts();
$active_posts = new ArrayCollection();
foreach ($all_posts as $post) {
if ($post->getActive()) {
$active_posts->add($post);
}
}
return $active_posts;
}
However, this requires me to load ALL posts into my entity manager, when I really only want a small subset of them, and it requires me to do filtering in PHP, when it would be much more appropriate to do so in the SQL layer. Is there any way to accomplish what I'm looking to do inside the Entity, or do I have to create code outside of it?
I think you should implement the method on the PostRepository rather than on the entity model.
I try to keep all model related logic in the repositories behind "domain specific" methods. That way if you change the way you represent whether a post is active or not, you only have to change the implementation of a single method instead of having to find all the active = true statements scattered around in your application or making changes in an "unrelated" entity model.
Something like this
PostRepository extends EntityRepository {
public function findActiveByUser($user){
// whatever it takes to get the active posts
}
}

Resources