In ASP.NET MVC I'm thinking to implement a method that converts data to JSON. However, I'm not sure where to add this method. Should I add it in Controller, service class, or Model? So, for example, let's say I have a model named book and a controller named Library.
public class Book
{
public string Name { get; set; }
public string Json { get; set; }
}
public class Library: Controller
{
public ActionResult Book(string bookName)
{
return View();
}
}
Should I add a private method like JsonConverter(object jsonToConvert) in Book class and call it in the constructor of Book? So, a user will pass an object to be converted to Book class, and then when instantiated it will be converted to Json and assigned to Book.Json.
Is adding methods in a model a bad practice? Is it better to add a service class that has JsonConverter and do the conversion in the controller?
So, like
public class Library: Controller
{
public ActionResult Book(string bookName)
{
var book = GetBook(bookName)
var json = Service.ConvertJson(book.object)
return View(book, json);
}
}
To sum up my question: is it bad practice to add private methods in a model and manipulate data format? The reason why I don't want to do the conversion in the controller is that I think it is a bad practice to convert data format in a class as the data layer is Model. Am I wrong?
The idea is to keep layers abstract from each other, and keep in mind Single-Responsibility principle, a class should do one thing, and one thing well.
In that regard, your model is responsible for defining the data schema. An instance of a model would hold the data. And that's it.
So this is your model
public class Book
{
public string Name {get; set;}
public string Json {get; set;}
}
In this case outputting an instance of a Book is an endpoint concern. It's not related to your business logic which concerns your services.
So keeping it in the controller like
public class Library: Controller
{
public ActionResult Book(string bookName)
{
var book = GetBook(bookName)
var json = Service.ConvertJson(book.object)
return View(book, json);
}
}
is what you can do best.
Whenever you are trying to achieve something ask yourself: Whose responsibility should this be?
Controller: Think of this like the waiter who greets you, shows you to your table and gets your order in a restaurant.
Service: This is the chef who cooks your meal.
Model: This is the meal you are going to order in the menu.
Converting to JSON is related to serving, so this responsibility goes to the controller.
Related
So, lets look at an example. I have a Customer view model that looks like this:
public class CustomerViewModel {
public string Name {get; set;}
public int CustomerTypeId {get; set;}
}
On the UI there needs to be a drop down of customer types. In order to populate this, something needs to go to the business layer or data layer and grab this list of customer types.
It seems to me that this logic doesn't really belong in CustomerViewModel since it isn't really customer data; only CustomerTypeId is. So my question is, where in general (not necessarily in .net MVC, but general practice for the MVC view model concept) do people put this data access?
Is it acceptable to have a function in the view model itself called GetCustomerTypes()?
public class CustomerViewModel {
public string Name {get; set;}
public int CustomerTypeId {get; set;}
public List<CustomerType> GetCustomerTypes() { ... }
}
Should I make a class/method in the business layer or a helper and import the code manually in the view to access this function? It seems to me like this would be code that clutters up the view and doesn't belong there.
#{
using My.App.CustomerTypeHelper;
var helper = new CustomerTypeHelper();
var customerTypes = helper.GetCustomerTypes();
}
...
#Html.DropDownFor(x => x.CustomerTypeId, customerTypes)
The global Html helper eases this problem a bit in .Net, but I am looking for a more global solution conceptually that I can apply to PHP code, etc also. PHP doesn't have the ability to cleanly have a static class that can be separated into small organized units with extension methods. So any global helper is likely to get large and ugly.
You should include List<CustomerType> in the model but do NOT implement function inside the model. Set the data from controller instead.
Something like this:
public class CustomerViewModel {
public string Name {get; set;}
public int CustomerTypeId {get; set;}
public List<CustomerType> CustomerTypes { get; set; }
}
Assign data to ViewModel from Controller:
var model = new CustomerViewModel();
model.CustomerTypes = Customer.GetCustomerTypes();
You could have a class that is 'shared' between models that has all the definitions of such data. I would go with something such as:
public static partial class StaticData
{
public static List<CustomerType> CustomerTypes = new Lisr<CustomerType>
{
new CustomerType { Name = "Whatever", Discount = 10, ....... },
new CustomerType { Name = "Another", Discount = 0, ........}
// etc
}
}
Notice this is a partial class so you can split this across files/folders in you project and have:
CustomerTypes.cs
SupplierTypes.cs
ProductTypes.cs
And anything else as separate files all building into a shared StaticData class that end up containing all your definitions for drop-downs and any other non-database information.
So then, in your view, you can populate your select options using StaticData.CustomerTypes.
The CustomerModel (not ViewModel, because there is no ViewModel in MVC) is not the model of a Customer. It is the model used in the view Customer. If that view needs this information, it should be in that model.
It is not clear to me what that view does in your application, but it looks like some customer creation form. Just calling it CustomerModel doesn't explain the intent of the class very well. You might want to call your model CreateModel, used in Create.cshtml that is returned from the Create() method in the CustomerController. Then it makes sense to add the CustomerTypes to this model: you need to have CustomerTypes if you want to create a customer.
I have a view which contains data from 4 tables from the database.
Do i need to define all of those fields in my model? so that i can use them in my view like :-
#model.fieldFromTable1
#model.fieldFromTable2
#model.fieldFromTable3
#model.fieldFromTable4
The quick answer is yes. You can probably think of your model more as a ViewModel. Not an actual model from your database.
In your controller you would just populate the ViewModel from your database models
MyViewModel.cs
//put whatever properties your view will need in here
public class MyViewModel
{
public string PropertyFromModel1 {get; set;}
public string PropertyFromModel2 {get; set;}
}
MyController.cs
public ActionResult MyAction()
{
//the only job your action should have is to populate your view model
//with data from wherever it needs to get it
Model1 model = GetFirstModelFromDatabase();
Model2 model2 = GetSecondModelFromDatabase();
MyViewModel vm = new MyViewModel
{
PropertyFromModel1 = model.MyProperty;
PropertyFromModel2 = model2.MyProperty;
}
return View(vm);
}
MyAction.cshtml
#Model.PropertyFromModel1
#Model.PropertyFromModel2
It's actually pretty standard practice to not use your raw domain models in your views, because I would say that typically don't match up exactly to what you want to display.
I have an object that I would like to display in a Details view. The object has a bunch of properties that the view needs.
The object also has parents and grandparents, which I need to display in the view.
What I have for my object viewModel is:
public class ObjectViewModel
{
// Used when creating a new object under a parent object
[HiddenInput(DisplayValue = false)]
public int? ParentObjectId { get; set; }
[Required]
public Object Object { get; set; }
// Info that only the view needs, which is defined in the Controller based on some logic
public string ActiveTitle { get; set; }
// A bre
public IList<Object> ParentObjects { get; set; }
}
I then use this in my Detail controller method:
public ActionResult Detail(int objectId)
{
// TODO: Make this a service call
var object = _db.Objects.FirstOrDefault(s => s.ObjectId == objectId);
if (object == null)
{
return View("Error");
}
var model = new SetViewModel() {
ActiveTitle = object.Name,
Object = object,
ParentObjectId = object.ParentObject.ObjectId,
ParentObjects = _objectService.GetParentObjects(set.ParentObject)
};
return View(model);
}
Does this look right? Or should I be pulling the required fields from the Object model into the viewModel, and not the objects themselves?
To have an object type in your view model is super vague and your code would be hard to support if you are not the original programmer. I would Add the class type to the actual model or use generics to specify the class type as shown below:
public class ObjectViewModel<T>
{
// Used when creating a new object under a parent object
[HiddenInput(DisplayValue = false)]
public int? ParentObjectId { get; set; }
[Required]
public T Object { get; set; }
// Info that only the view needs, which is defined in the Controller based on some logic
public string ActiveTitle { get; set; }
// A bre
public IList<T> ParentObjects { get; set; }
}
Either option will work, and you will often have a mixture of both techniques in a given application.
The key idea is that your view model should contain what the view needs in order to display the data to the user.
If your view merely displays the individual, primitive fields in simply controls, e.g. a series of labels or textbox controls, then your view model should probably specify only the fields, and not the parent object.
However, it's very possible for your view to include a templated or custom control that "knows how" to display a complex object in its entirety. In that case, your view model would need to include the entire object. (In practice I find myself doing this much more often in WPF than ASP-MVC but I've done both).
It looks like the answer will be contextual. Many teams using layered architectures might adopt architectural conventions whereby layers below X should not be referenced directly by your views, and a data access class might be a likely candidate for such a restriction. In your case, it does look like you will be binding your view's structure to the structure directly to your database schema (assuming, since you're using "_db"), which might be considered unreasonably tight coupling.
Also, I'm assuming you're using "object" to represent "any general thing" rather than literally a System.Object, since your objects appear to have an ObjectId property in your lambda expression.
I'm starting to use AutoMapper and some doubts arose.
Where is the correct way to map a dto into a domain model?
I'm doing this:
DTO:
public class PersonInsert
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(PersonInsert personInsert)
{
if (ModelState.IsValid)
{
new PersonService().Insert(personInsert);
return RedirectToAction("Insert");
}
return View("Insert");
}
Service:
public class PersonService
{
public int Insert(PersonInsert personInsert)
{
var person = Mapper.Map<PersonInsert, Person>(personInsert);
return new PersonRepository().Insert(person);
}
}
Repository:
public class PersonRepository
{
internal int Insert(Person person)
{
_db.Person.Add(person);
_db.SaveChanges();
return person.Id;
}
}
So, is this correct? should my service knows about domain? or should I make the bind in repository only? is correct to use [Required] in DTO?
I would almost never create an entity from a DTO - I explain why below. I would use a request object to allow a factory method to build the entity:
Request:
public class InsertPersonRequest
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(InsertPersonViewModel viewModel)
{
if (ModelState.IsValid)
{
InsertPersonRequest request = InsertPersonViewModelMapper.CreateRequestFrom(viewModel);
new PersonService().Insert(request );
return RedirectToAction("Insert");
}
return View("Insert");
}
Service:
public class PersonService
{
public int Insert(InsertPersonRequest request)
{
var person = Person.Create(request.name, request.LastName);
return new PersonRepository().Insert(person);
}
}
Repository stays the same.
This way all logic for creating the Person are located in the Factory method of the person, and so business logic is encapsulated in the domain - derived fields, default fields etc.
The problem with what you are doing is that the DTO has to be created in the UI, then all fields are mapped to the entity - this is a sure fire way for business logic to seep into the service layer, UI, or anywhere it is not supposed to be.
PLease read that again - This is a very serious mistake I see made time and time again.
I would however, use AutoMapper in the service layer to return a DTO:
Service:
public class PersonService
{
public PersonDto GetById(intid)
{
var person = new PersonRepository().GetById(id);
var personDto = Mapper.Map<Person, PersonDto>(person);
return personDto
}
}
Is this correct?
I personally don't see anything wrong with having your service do the mapping
Is it correct to use [Required] in DTO
No, DTOs should have no business logic whatsoever. They should be used purely for transmitting data across different tiers/layers of your application.
DataAnnotations are typically used on ViewModels for client/server side validation, therefore, I would add another separation into your model and introduce a ViewModel for your Insert action e.g.
public class PersonViewModel
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(PersonViewModel personViewModel)
{
if (ModelState.IsValid)
{
var personDto = Mapper.Map<PersonViewModel, PersonDto>(personViewModel);
new PersonService().Insert(personDto);
...
}
...
}
}
Service:
public class PersonService
{
public int Insert(PersonDto personDto)
{
var person = Mapper.Map<PersonDto, Person>(personDto);
return new PersonRepository().Insert(person);
}
}
It may seem overkill in this scenario (considering the only difference is the [Required] attribute). However, in a typical MVC application you would want to ensure a clean separation between your ViewModels and your business models.
I would say that your PersonService could be seen as part of the domain layer (or Application layer directly above the domain) of your architecture and the controller and DTO is in a layer above that. That means you shouldn't have a reference to the DTO in your PersonService signatures and instead use the domain Person class here. So the Mapping code should go into the Controller. This ensures that your domain logic is not affected by changes to the webservice contract which could really be just one way to use your PersonService.
I would also introduce an interface for your repository which is injected into your PersonService because the PersonService again shouldn't need to know about concrete data access implementations.
As for the [Required] attribute, I don't see a problem with having this on the DTO because it just states the data contract of your webservice method. Anybody calling your webservice should adhere to this data contract. Of course this requirement will typically also be reflected somewhere in your domain code, maybe by throwing an exception etc.
In ASP.NET MVC the typical use of DTO is being part of something called viewmodel. Viewmodel is a class that will combine one to several DTOs into one class tailored for view presentation and posting values back to server.
What you doing is correct, no issues with that, but data annotations should reside on view models, rather than DTOs. Unless you call your DTO a view model, then its fine.
Please read the following posting about model (Domain Model) vs ViewModel in ASP.NET MVC world:
ASP.NET MVC Model vs ViewModel
Confused with Model vs ViewModel
Hope this helps
I think it is fine to have annotations on the DTOs, such as [Required], MaxLength, Range etc.
Your DTO can come in from any (possibly untrusted) source (Not just your website, but from another endpoint, WCF service, etc). All requests will be funneled to your Service/Business Layers, so you will need to validate the input before performing your business logic (simple guard checks). Having the annotations on the DTO simply describe the needed input to perform the task at hand. Passing an object with annotations is not peforming validation.
However, I believe you should be validating the DTO information is correct in the service/business layer (and annotations are a nice way to check this).
Just my thoughts on the situation :)
I want a way to separate the loading of reference data into a view model from the controller. At the moment I have a view model with a property for the selected value and the reference data:
public IEnumerable<SelectListItem> DayTypes { get; set; }
public int DayTypeId { get; set; }
and the data is populated from the relevant repository in the controller action:
model.DayTypes = _dayTypeRepository.GetAll().ToSelectList(d => d.Description, d => d.Identifier.ToString());
I would like to change this because it pollutes the controller with lots of repositories and code that is not core to its concerns. All of these dependencies make unit testing the controller a pain.
One possible approach to solving this would be to make the view model class do the loading which would require a custom model binder to instantiate them using the IoC container to provide the repository dependency. Is this a good option?
Another approach that I think would be good is hinted at in CodeCampServer but is incomplete and commented out involving attributes on the field in the view model:
[SelectListProvided(typeof(AllDaysSelectListProvider))]
public IEnumerable<SelectListItem> DayTypes { get; set; }
however I am struggling to figure out how this could be implemented in a way that would not require some major replumbing of the MVC framework.
How do you solve this problem?
EDIT: I want to keep with strongly typed views and avoid stuffing the data into view data.
FURTHER EDIT: I would also like a solution that is ideally model independent, by which I mean if the same reference data is needed by multiple view models this can be achieved with a single piece of code. Matt's approach is interesting but is tightly coupled to the view model.
I would use a service layer which would return me a POCO object that I would map to a view model. So my controller action would look like this:
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
var viewModel = Mapper.Map<Model, ViewModel>(model);
return View();
}
I also like using action filters to avoid the mapping code all over again so:
[AutoMap(typeof(Model), typeof(ViewModel))]
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
return View(model);
}
This way only the service talks with the CRUD repositories and the controller talks to the service and the mapping layer.
You could write a new ActionFilter that you can decorate an action method with; this action filter will load the reference data into the viewdata, which you can access from your view.
There is more on action filters here.
EDIT: Based on the users comments, this now includes a strongly typed option.
Firstly, you need to create the SharedViewModel to contain the shared data.
public class SharedViewModel
{
public List<string> Days { get; set; }
public List<string> Months { get; set; }
public List<string> Years { get; set; }
}
Next, we create the view model to be used by the Index view, which uses this shared view model.
public class HomeViewModel
{
public string ViewName { get; set; }
public SharedViewModel SharedViewModel { get; set; }
}
The next step is important, it implements an action filter called SharedData(), which will apply the shared data.
public class SharedDataActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var currentModel = ((HomeViewModel) filterContext.Controller.ViewData.Model);
currentModel.SharedViewModel = new SharedViewModel
{
Days = new List<string> {"Mon"},
Months = new List<string> {"Jan"},
Years = new List<string> {"2011"}
};
base.OnActionExecuted(filterContext);
}
}
At the moment, it just applies the whole shared data, but you can added parameters into the method to be selective.
When the action has been executed, this method takes the current model and adds the shared data.
Here is the controller action.
[SharedDataActionFilter]
public ActionResult Index()
{
return View("Index", new HomeViewModel { ViewName = "HomePage" });
}
You can access the data like any other strongly typed view, and the shared data wont affect the data already in the model (in this case "ViewName"). You can also use action filters across controllers, and globally across the site with mvc 3.
Hope this helps, Matt.