Is this an MVC anti-pattern? - c#

I’m very new to any kind of .NET web development (thus far I’ve worked primarily with Winforms and services.) I’ve started to work on an existing MVC3 project with two other developers. I’m conceptually familiar with MVC, and am trying to catch up on how it’s used in this project.
We have an AccountDto class to represent Accounts. There is a Response class that is inherited by another class for each Entity, i.e. AccountResponse:
public class Response
{
[DataMember]
public bool IsSuccess{get;set;}
[DataMember]
public string DisplayMessage { get; set; }
[DataMember]
public string DetailedMessage { get; set; }
[DataMember]
public ErrorType ErrorType { get; set; }
public Response(){
this.IsSuccess=true;
this.ErrorType = ErrorType.None;
}
}
public partial class AccountResponse : Response
{
[DataMember]
public IList<AccountDto> AccountList { get; set; }
}
There’s an AccountService which will return an AccountResponse to the Controller, with a list of the AccountDto object:
public AccountResponse GetAccountByAccountId(Int64 accountId)
{
_logger.Info("Executing GetAccountByAccountId()");
AccountResponse response = new AccountResponse();
try
{
Account item = AccountPersistence.GetAccountByAccountId(accountId);
AccountDto dto = Mapper.Map<AccountDto>(item);
response.AccountList = new List<AccountDto>() { dto };
response.IsSuccess = true;
}
catch (Exception ex)
{
response.IsSuccess = false;
response.ErrorType = ErrorType.GeneralFault;
response.DetailedMessage = ex.ExceptionMessageBuilder();
response.DisplayMessage = "System Failure: Failed to get Account by AccountId";
_logger.Error(ex);
}
return response;
}
I was told the Response thing is implemented to be able to handle success/failure messages. So in a controller, there’s code like the following (doesn’t happen to do anything special if a failure):
public ActionResult ToBeCalled(int id)
{
AccountDto dto = null;
var response = _accountService.GetAccountByAccountId(Convert.ToInt64(id));
if (response.IsSuccess)
{
dto = response.AccountList[0];
return View(dto);
}
return View(dto);
}
This made sense to me though I wasn’t sure where the success/error messages were going to be utilized. However, they now want to switch from using the DTO in views to using the Response, so success/failure will have to be handled in the views:
public ActionResult ToBeCalled(int id)
{
var response = _accountService.GetAccountByAccountId(Convert.ToInt64(id));
return View(response);
}
This seems off to me – instead of coding against a DTO as the model, I have to do something like the following for each page:
#{
if (Model.IsSuccess)
{
var account = Model.AccountList.FirstOrDefault();
if (account != null)
{
#Html.HiddenFor(x => account.AccountNumber)
}
}
The controllers’ ActionResult / HttpPost methods then have to also parse the DTO from these Response objects. This seems like an anti-pattern to me; are approaches like this normal?
Apologies if this is too lengthy, please migrate if it belongs on Code Review or another site.

I agree with you that this would be an anti-pattern. The View is supposed to be quite ignorant, especially of logic like this.
I can see why this would be tempting, if the difference between success and failure is a minor part of the UI, but imagine if that were to change. A view has little ability (without unnecessary nesting of partials) to switch to an entirely different view. It has no ability to issue a redirect or other error codes. In the event that you decide to change your UI, you may have to go back and rewrite your Controller yet again.
If the reasoning behind moving the logic to the view was to remove the response.IsSuccess logic from the Controller (and to be honest, that seems fine to me; it's pretty much the same as the classic Model.IsValid), you could consider another approach: refactor your Response class to inherit from ActionResult. Then you could move that logic into the ExecuteResult() method and it would be separate from your Controller.

Just use the coalesce operator and you can get rid of a whole lot of cruft (like that strange Response base class (which should be marked abstract if it continues to exist)) and avoid null-checking.
public ActionResult ToBeCalled(int id)
{
var response = _accountService.GetAccountByAccountId(id) ??
new AccountResponse();
return View(response);
}
Better yet, migrate that logic into your service class so that it guarantees return of an object (it doesn't necessarily make sense for a repository to do this when there's no backing entity, but it does for a service).
Either way, you don't need to include unsightly null-checking or if/else logic on your view. Move as much logic to places that you can test it as you can and you'll be happier.

Related

ASP.NET MVC 6 Core - Validation issue / bug?

I've migrated an MVC4 app to MVC6 (both .NET 4.6.1) and am hitting numerous errors with the inbuilt model validation.
I have a number of complex models that are posted to controllers, and unless I disable validation on each model under configure services, they throw unnecessary exceptions relating to properties that are irrelevant to validation, or just hang after postback without reaching the controller action.
I have added the following line to MVC Configuration for all my affected classes, but I've now got a model that requires validation, so turning it off will cause numerous code changes.
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(TestModel)));
I tried this with a test app and can replicate the issue:
Test Controller:
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(TestModel model)
{
return View();
}
Test Model (for example)
public class TestModel
{
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public int NameLength
{
get
{
return Name.Length;
}
}
}
Without the validation attributes, the code works fine, but is not validated (obviously).
But when this model is posted, a NullReference exception is thrown by the NameLength property, even though no code references it, the property is read only, and the property it depends on is required. This validation happens before control is returned to the controller.
I've tried disabling this functionality in MvcOptions, but it doesn't have any effect:
options.MaxValidationDepth = null;
options.AllowValidatingTopLevelNodes = false;
options.AllowShortCircuitingValidationWhenNoValidatorsArePresent = true;
I don't know if there's a setting I'm missing, but I would expect the default functionality to ignore properties without validation attributes, or am I doing something wrong?.
Thanks in advance for your help.
Further to #Henks suggestion, I've added the ValidateNever attribute to the readonly properties of one class I was having problems with, which has worked, so the postback reaches the controller now, but its still calling the properties, it just seems to ignore the result:
[ValidateNever]
public Competition PrimaryCompetition
{
get
{
return GetCompetition(true);
}
}
This still triggers a null reference exception because it relies on another property that is [Required] but is not validated first.
I'm beginning to think this is a bug rather than an error on my part.
Why this happens
I haven't seen this issue with simple types (like in some of the example code you posted), but we just had a similar issue with complex types.
From looking at the source code, this has to do with how the complex model binder works. It steps through every public property getter that is a complex type (e.g. a class) when posted regardless of whether the property was used at all. I think this may be an intentional choice by Microsoft because it is possible that the underlying properties of a complex type could be settable.
For example if your Competition class and PrimaryCompetition property on another class (called Test here) looked like this:
public class Competition
{
public string Name { get; set; }
public List<string> Roster { get; set; } = new List<string>();
}
public class Test
{
public Competition PrimaryCompetition
{
get
{
return AllCompetitions.First();
}
}
public List<Competition> AllCompetitions { get; set; } = new List<Competition>();
}
Underlying properties of PrimaryCompetition can be modified even though it has no setter:
var competition = new Competition {
Name = "Soccer"
};
competition.Roster.Add("Sara");
var test = new Test();
// This code outputs "Sara"
test.AllCompetitions.Add(competition);
Console.WriteLine(test.PrimaryCompetition.Roster[0]);
// This code outputs "Amanda"
test.PrimaryCompetition.Roster[0] = "Amanda";
Console.WriteLine(test.PrimaryCompetition.Roster[0]);
Possible solutions
Make the property a method instead:
public Competition PrimaryCompetition() => GetCompetition(true);
Make the property internal instead of public:
internal Competition PrimaryCompetition
{
get
{
return GetCompetition(true);
}
}
Add the ValidateNever and BindNever attributes to the property:
[BindNever]
[ValidateNever]
public Competition PrimaryCompetition
{
get
{
return GetCompetition(true);
}
}
We decided to go with option 1 since in Microsoft's best practices they recommend not throwing exceptions from getters Property Design.
Property getters should be simple operations and should not have any preconditions. If a getter can throw an exception, it should probably be redesigned to be a method.

Validation in a layered application ASP.NET Core

I'm designing a layered web application with an MVC, Service and Repository layer, however I'm having trouble knowing where to put validation logic that allows me to take advantage of .NET Core built in form validation (eg ModelStateDictionary), while following the DRY principle.
The first and most obvious approach is to use a ViewModel that has the appropriate data annotations:
public class VendorViewModel
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Address { get; set; }
public DateTime? VerifiedAt { get; set; }
}
Then my controller action would look like this
public async Task<IActionResult> Create([FromForm] VendorViewModel model)
{
await AuthorizePolicyAsync(AuthorizationPolicyTypes.Vendor.Create);
if (!ModelState.IsValid) //Validation problems, so re-display the form.
return View(model);
await _vendorservice.CreateVendorAsync(model.Name,model.Phone,model.Email,model.Address,null);
return RedirectToAction(nameof(Index));
}
This works fine, however there are a couple problems:
This only supports basic validation such as checking character length, etc. In the particular example above, I want to validate that model.Address is a valid address according to google maps and also contains a city that the application is aware of, which means this kind of validation should be moved to the service layer to keep the Controller "thin".
The service layer is now missing any validation logic, and assumes that it is always being passed valid data. This seems wrong to me since it seems like the service layer should be responsible for keeping the system in a consistent valid state. A solution to this would be to also add validation logic to the service layer, but that seems to violate the DRY principle in my opinion.
The second approach would be to move all of the validation logic to the service layer and move all my data annotations to the actual domain object Vendor. This way each operation could validate the model based on the data annotations, and also apply any more complex logic such as validating the address with google maps as previously mentioned. However, I'm not sure how I can validate an annotated object in the same manner that a MVC Controller does and pass back a dictionary to the controller. This functionality seems to be specific to MVC and would introduce a dependency on MVC in my service layer which is undesirable.
Is there anyway I can elegantly move validation logic to the service layer while
taking advantage of data annotations and MVC's built in ModelStateDictionary? How do I get the list of errors back to the controller? Do I throw an exception and catch it in the controller if any validation errors occur?
I have seen several questions asking a similar question, but I'm not satisfied with any of the answers. Other answers seem to involve writing validation logic manually and not taking advantage of data annotations. Is this what I should resort to?
You can create your own custom validation attributes in addition to what are available out of the box such as Required,Range,StringLength,etc.
I will provide an example below :
public class ValidateAddressAttribute : Attribute, IModelValidator
{
public bool IsRequired => true;
public string ErrorMessage { get; set; } = "Address is not valid";
public IEnumerable<ModelValidationResult>Validate(ModelValidationContext context)
{
List<ModelValidationResult> validationResults = new List<ModelValidationResult>();
string address = context.Model as string;
if(!IsAddressValid(address))
{
validationResults.Add(new ModelValidationResult("", ErrorMessage));
}
return validationResults;
}
private bool IsAddressValid(string address)
{
bool isAddressValid;
//set isAddressValid to true or false based on your validation logic
return isAddressValid;
}
}
You can now apply this attribute on your address property as follows :
[Required]
[ValidateAddress(ErrorMessage="Invalid Address")]
public string Address { get; set; }

C# MVVM - Model to access ViewModel functions

I'm working on a REST API implementation in MVVM for Windows Universal Apps.
It is going fine, but I would like to solve one thing.
I have a Post and a Comment class on Model level, and they have Downvote and Upvote functions. The API calls are implemented in the ViewModel, including the calls that tell the server to do the downvote-upvote.
I would like to have the Models Downvote/Upvote functions to trigger the ViewModel's appropriate calls. Is it possible or am I going around it the wrong direction?
You're stuck where most programmers who begin with MVVM are stucked. You just look only at MVVM and strictly ignoring everything else.
What you see is: Model, ViewModel and View and you try to put all your business logic into one of these.
But the Model part is more than POCO Objects with a little bit of logic. Services also belong to the model. This is where you encapsulate all business logic which doesn't belong into a certain model.
You could implement a PostService class and an CommentService class, which both implement the UpVote/DownVote functionality and call these services from your ViewModel.
public interface ICommentService
{
void UpVote(Post post, Comment comment);
void DownVote(Post post, Comment comment);
}
public class CommentRestService
{
IRestClient client;
public CommentRestService(IRestClient client)
{
this.client = client;
}
public void UpVote(Post post, Comment comment)
{
var postId = post.Id;
var commentId = comment.Id;
var request = ...; // create your request and send it
var response = request.GetResponse();
// successfully submitted
if(response.Status == 200)
{
comment.VoteStatus = VoteType.Up;
comment.Score += 1;
}
}
public void DownVote(Post post, Comment comment)
{
var postId = post.Id;
var commentId = comment.Id;
var request = ...; // create your request and send it
var response = request.GetResponse();
// successfully submitted
if(response.Status == 200)
{
comment.VoteStatus = VoteType.Down;
comment.Score -= 1;
}
}
}
In your ViewModel you simply pass the service via Dependency Injection or get it via ServiceLocator and then use it's method, rather than calling UpVote/DownVote on the model.
// in ViewModel
// get via ServiceLocator or DI
ICommentService commentService = ...;
commentService.UpVote(this.Post, this.SelectedComment);
You could also implement this methods on the Model, to encapsulate the actions into the Comment class, i.e. by making Score and VoteStatus "private set;"
public class Comment
{
public string Comment { get; set; }
public Post Post { get; private set; }
public VoteType VoteStatus { get; private set; }
public int Score { get; private set; }
public void UpVote(ICommentService commentService)
{
// for this you'd change your Up/Vote method to return only true/false and not
// change the state of your model. On more complex operation, return an CommentResult
// containing all necessary information to update the comment class
if(commentService.UpVote(this.Post, this))
{
// only update the model, if the service operation was successful
this.Score++;
this.VoteStatus = VoteType.Up;
}
}
}
And call it via
SelectedComment.UpVote(commentService);
Later method is preferred, as you have more control of the Comment object and the Comment's state can only be modified via Comment and it's methods class. This prevents from accidentally changing this value somewhere else in code and receive inconsistent state (i.e. changing VoteStatus without incrementing the Score value).
Model
--> data and it's associated up-/ downvote information.
ViewModel
--> Implementation of up-/ downvoting.
View
--> triggers the up-/ downvote (e.g. commands).
Dependencies are chained like this View -> ViewModel-> Model. Thats the pattern.

Why controllers run first in the ASP.NET MVC?

I want to improve current implementation of the ASP.NET MVC Framework.
Current code:
routes.MapRoute(null, "I-want-to-fly", new { controller = "Airport", action = "Fly" });
public class AirportModel
{
public List<Plane> Planes { get; private set; }
public List<Pilot> Pilots { get; private set; }
public void AddFly(Plane plane, Pilot pilot, Passenger passenger)
{
// . . .
}
}
public class AirportController
{
private AirportModel model;
[HttpGet]
public ViewResult Fly(string from, string to)
{
var planes = return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
return View(planes);
}
[HttpPost]
public ActionResult Fly(Plane plane, Passenger passenger, DateTime time)
{
if (!(ModelState.IsValid && plane.TimeOfDeparture == time))
return View();
var pilot = (from p in model.Pilots
where p.Free && p.CanAviate(plane.Id)
select p).First();
model.AddFly(plane, pilot, passenger);
return RedirectToAction("Succeed");
}
}
My proposal:
routes.MapRoute(null, "I-want-to-fly", new { model = "Airport", action = "Fly" });
public class AirportModel
{
private List<Plane> planes;
private List<Pilot> pilots;
private void AddFly(Plane plane, Pilot pilot, Passenger passenger)
{
// . . .
}
[HttpGet]
public ViewResult Fly(string from, string to)
{
var planes = return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
return View(suitablePlanes);
}
[HttpPost]
public ActionResult Fly(Plane plane, Passenger passenger, DateTime time)
{
if (!(ModelState.IsValid && new PlaneController().CanFly(plane, time)))
return View();
var pilot = (from p in pilots
where p.Free && p.CanAviate(plane.Id)
select p).First();
AddFly(plane, pilot, passenger);
return RedirectToAction("Succeed");
}
}
public static class PlaneController
{
public static bool CanFly(Plane plane, DateTime time)
{
return plane.TimeOfDeparture == time; // it will be more complex
}
}
You see, in such way we don't need excessive count of controllers and their methods. Model would create controller only by perforce: mostly to verify user input (not input validation, business validation).
What do you think, can this idea have a continuation? Or, what is wrong with it?
Thanks for your replies!
UPDATE: I noticed, that we need to replace implementations of controller and view as a result of changing the model's state (mostly). So, if model causes to change the implementation, why model cannot do it?
UPDATE 2: It seems to me I explained incorrectly. I don't want model to do all work, of course no! I try to say, that not controller should decide what to do with model and what view is the most suitable for this user request.
Doesn't it strange, that model doesn't know how to visualize itself, but some controller knows?
Doesn't it strange, than we need controller for the GET request, where there is nothing to control?
I try to remove those strangenesses.
UPDATE 3: I understand that it cannot be applied anywhere. The main question is: can it improve some part of current implementations of MVC ? Mostly I'm interested in ASP.NET MVC -- can we
remove redundant controllers or some its methods
work directly with models
using this idea? Is it possible and what are the problems of this idea?
Found problems:
More strong connection between model and view/controller -- but currently I don't think it's a problem. Actually it shows that views and controllers were created in help for the major element -- model.
UPDATE 4: I changed the code, showing "before/after". Maybe this example will be better.
Doesn't this just violate the whole idea of MVC? Your model is separated from your controller and your view. In this way (the way you propose) you would not be able to replace your model by another implementation, or your controller for that matter.
updated I:
You could of course let your model do the part of the controller as well, but from that moment on you're not talking about the MVC design pattern anymore. For MVC, the model does and should not now about the view. That's the controllers job.
The controller receives user input and initiates a response by making calls on model objects. A controller accepts input from the user and instructs the model and viewport to perform actions based on that input.
In the MVC pattern, the Model isn't just fixed to your database model, it could be a combination of your database model and a repository pattern as well, where you implement your business logic.
The biggest problem I see with your proposal is that it makes code non-reusable. I get a model that is tightly coupled with it's views which I really don't want if I want to reuse the model in whatever way I might want to.
Update II
I think your are being mislead by the actual word Controller, I had that thought for a while and your latest comment sort of confirms this for me
Controllers are some objects, that check correspondence of user input to business-logic.
Controllers act upon user input, they might check the user input but their responsibility for checking validity stops there. Business logic goes in the Model (again, the Model as defined by the MVC pattern, not the model as in datamodel). Their main purpose is deciding what View to display.
Also from one of your latest comments:
How do you think, if [asp.net mvc] would be developed in my way, would it solve problem of redundant controllers?
Asp.Net MVC follows the MVC design pattern. Your proposal does not. It seem more like a ModelControlled View pattern, just to coin a name. Also, there are no redundant controllers, the controllers are no problem, they are an integral part of the solution.
And an effort to simplistically clarify what I mean with a code example:
namespace DataProject.Model
{
public class AirportModel
{
public List<Plane> Planes { get; set; }
public List<Pilot> Pilots { get; set; }
public List<Passenger> Passengers { get; set; }
public List<Flight> Flights { get; set; }
}
}
namespace SomeProject.Repository
{
public class AirportRepository
{
private DataProject.Model.AirportModel model;
//constructor sets the model somehow
public bool AddFlight(Plane plane, List<Passenger> passengers, DateTime time)
{
//Business logic
if (plane.TimeOfDeparture != time) return false;
var pilot = (from p in model.Pilots
where p.Free &&
p.CanAviate(plane.Id)
select p).FirstOrDefault();
//More Business logic
if (pilot == null) return false;
//Add plane, pilot and passenger to database
model.Flights.add(new Flight{Pilot = pilot, Plane = plane, Passengers = passengers});
//Even here you could decide to do some error handling, since you could get errors from database restrictions
model.Save();
return true;
}
public List<Planes> GetPlanes(string from, string to)
{
return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
}
}
}
namespace MVCApp.Controllers
{
public class AirportController
{
private SomeProject.Repository.AirportRepository repository;
[HttpGet]
public ViewResult Fly(string from, string to)
{
var viewModel = repository.GetPlanes(from, to);
return View(viewModel);
}
[HttpPost]
public ActionResult Fly(Plane plane, List<Passenger> passengers, DateTime time)
{
if (!ModelState.IsValid) return View();
if (!repository.AddFlight(plane, pilot, passenger)) return View();
return RedirectToAction("Succeed");
}
}
}
No offense intended but how exactly is this an improvement?
So you've made a class called a PersonModel that isn't really doing "model things" at all - it is doing the work that Controllers do - you've got it handling gets and posts and calling out for the display of Views and then you've got a static "Controller" that really controlling nothing and is concerning itself with business logic. Honestly, I don't get how this is an improvement.
A concrete example of is you've got a controller checking whether Age >= 18, which is a very "business rules" thing for a controller to be doing. That's not the purpose of a controller. That's the job of a model object - to concern itself with things like business logic. Controllers, as one person put it, are more of an electronic curator. In your example, you've relegated it to something far less than a curator.
There are distinct roles that objects play in an MVC application. Views show us stuff and provide us with ways to interact with the application. Controllers handle the input coming from the View and serve up views that are needed. Models provides a place to put data and the logic and business rules that the model encompasses. Services handle things like persisting data to some store, like a DB.
You can do everything in one class without controllers at all but you want "separte of concerns"
So the controller is responsible for the http request and validation and the model is responsible only for the data.
You are the programmer then you could agree or disagree with MVC pattern.
But the pattern which you described doesn't support the separation of concern and it breaks out the whole idea about MVC
This has nothing to do with MVC.
This is 'MVNothing'
:)
Just kidding *_^

How to avoid placing domain logic in controller?

On PRO ASP.NET MVC book:
It is certainly possible to put domain
logic into a controller, even though
you shouldn’t, just because it seems
expedient at some pressured moment.
Just a contrived example, if the application doesn't allow negative order, where to put the changing of quantity to 1? If we follow the principle that domain logic shouldn't be placed in controller, this is certainly not advisable to use:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PlaceOrder(Order order)
{
if (ModelState.IsValid)
{
order.Submit();
return View("Thanks", order);
}
else
{
if (order.Quantity <= 0)
{
ModelState.Remove("Quantity");
order.Quantity = 1;
}
return View(order);
}
}
So the following code is the right code that adheres to MVC principle, i.e. it follows separation of concerns, if it's domain logic you should not see its code in controllers. So this is how I tried placing the domain logic in Model:
public class Order : IDataErrorInfo
{
public int OrderId { set; get; }
public int ProductId { set; get; }
public int Quantity { set; get; }
public string Error { get { return null; } }
public string this[string propName]
{
get
{
if (propName == "Quantity" && Quantity <= 0)
{
Quantity = 1;
return "0 or negative quantity not allowed, changed it to 1";
}
else
return null;
}
}
}
Controller (sans the domain logic):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PlaceOrder(Order order)
{
if (ModelState.IsValid)
{
order.Submit();
return View("Thanks", order);
}
else
{
// Response.Write(order.Quantity.ToString()); // this was changed in Model
return View(order); // but the View didn't reflect that fact
}
}
The only problem with that approach, the Model(Order) can't influence the ModelState, and such, the program always display whatever is last entered by the user.
What's the best approach so I can still avoid placing domain logic in controller and the View is still able to reflect the values of Model's properties?
Validation is not controllers task. You can Put all required logic in different module, and just propogate requests there.
Ah, but the business layer can influence model state. Check out this tutorial on validating with a service layer. It's also a great intro to repository and Inversion of Control.
The general approach is to create a wrapper for the model state that implements a simple interface for adding errors to the model state. Your business layer acts against the interface - thus it has no ties to your modelstate. Your unit tests can implement a fake wrapper that also implements that same interface.
It looks like your specific example is changing the user's invalid input to valid input. My suggestion would be to simply leave the invalid input and use AddModelError to reflect that when the controller returns the view.

Categories