I'm quite new to ASP.NET and MVC and I get confused about the CRUD logic. I found the following explanation from a resource, however, as far as I know, the CRUD are performed in controller, for instance, they are achieved by the actions such as Index, Create, DeleteConfirm, Edit, etc. Am I misunderstanding this concept?
According to the picture above, the model is the "Doamin Model", it is not "M" from MVC as Slava Utesinov said, it's a concept of DDD(Domain-Driven Design).
In ASP.NET MVC, the traditional way that where we do CRUDs are in actions of controllers, and your understand is right.
In DDD concept(Domain-Driven Design), we do CRUDs in domain model.
Whatever we use DDD architecture or traditional ways, we need to do that based on the basic MVC architecture.
More information about DDD for your reference:
Domain-Driven Design – What is it and how do you use it?
Yes you are right, CRUD operations can perform in Controller by actions and Model can help to achieve that. Model is nothing but a class which will having properties in that.
For example: "Employee" is class which is having different properties like "FirstName, LastName, EmployeeID, Email, DateOfJoining etc.". Now if you have to perform CRUD operation on this then you have to write code in Controller Class under different actions by using this "Employee" model class.
Model only cannot perform CRUD operation.
You can do it wherever you want it but you will be breaking the concept which is "Separation of Concerns". The Controller should only be concern of which view or which action it is going to call or perform. The Model should only be used how your data is going to be structured, it is usually similar to how your database properties look. In short, your Model (class model) should have minimal thinking. For example, you have a table called Person with columns as IDPerson, FirstName,LastName. Your model should be something similar to this:
public class Person {
public IdPerson {get;set;}
public FirstName {get;set;}
public LastName {get;set;}
}
Let's say you have a view that shows the detail of a person which could be something
like this:
public class PersonController : Controller
public ActionResult GetPerson(int IdPerson){
PersonBusinessLogic pbl = new PersonBusinessLogic();
Person p = pbl.GetPersonFromDatabase(id); //To add more consistency, the data access is on a separate class for better maintenance and to emphasize "Separation of Concerns"
ViewResult vr = new ViewResult()
{
ViewName = this.View,//This is where you assign the page if you have other pages for this action
ViewData = new ViewDataDictionary(ViewData)
{
Model = p
}
};
return vr;
}
For your crude:
[HttpPost]
public ActionResult CreatePerson(Person p)
{
try
{
if (ModelState.IsValid)
{
PersonBusinessLogic pbl = new PersonBusinessLogic();
pbl.CreatePersonToDatabase(p);
return RedirectToAction("Index", "Home");
}
}
catch(Exception ex){
ModelState.AddModelError("",ex.Message);
}
return View(p);
}
[HttpPost]
public ActionResult UpdatePerson(Person p)
{
try
{
if (ModelState.IsValid)
{
PersonBusinessLogic pbl = new PersonBusinessLogic();
pbl.UpdatePersonToDatabase(p);
return RedirectToAction("Index", "Home");
}
}
catch(Exception ex){
ModelState.AddModelError("",ex.Message);
}
return View(p);
}
[HttpPost]
public ActionResult DeletePerson(Person p)
{
try
{
if (ModelState.IsValid)
{
PersonBusinessLogic pbl = new PersonBusinessLogic();
pbl.DeletePersonByIDFromDatabase(p.IdPerson);
return RedirectToAction("Index", "Home");
}
}
catch(Exception ex){
ModelState.AddModelError("",ex.Message);
}
return View(p);
}
To give you a better idea, Find some article on how MVC is greatly applied as a concept then you will greatly appreciate the learning process.
Related
I am asking this because after long time searching I haven't found a good answer on this yet...
Here is what I want:
Example: I have a domain model "JobPosting" which a user should be able to change state to published, if it is still a draft. Before publishing I must not only validate the model properties I must also validate many different requirements regarding the user account, it's registered company etc. All this validation logic is put into a service layer. So far so good...
This is how my service layer looks like:
public IValidationResult ValidatePublish(JobPosting jobPosting){
...
}
public void Publish(JobPosting jobPosting){
jobPosting.State = JobPostingState.Published;
...
}
Any my controller:
public ActionResult Publish(PublishViewModel model){
...
var validationResult = _jobService.ValidatePublish(jobPosting);
if(validationResult.Success){
_jobService.Publish(jobPosting);
...
}
...
}
And here now my questions:
I want to be able to call the ValidatePublish from the controller to show validation errors in the view. However I must never be able to publish a job when validation fails.
So to have my code more robust I added a second validation check in my Publish method in service layer:
public void Publish(JobPosting jobPosting){
if(ValidatePublish(jobPosting).Success){
jobPosting.State = JobPostingState.Published;
...
}
}
but I have not such a good feeling with this approach because now I am calling the validation twice when validation is OK during each controller publish request.
What do you think. Is the second call to much? Is there a better approach?
I am asking because my whole application looks like that and if I would ever forget a validation call in controller I might end up with an not allowed domain model state in database. That's why I added the second validation check in each service method.
Thanks in advance for your thoughts on this!!!
One quick solution might be to have the Publisher class require the JobPosting and IValidationResult objects as arguments.
public void Publish(JobPosting jobPosting, IValidationResult validation)
{
if (validation.IsValid)
{
jobPosting.State = JobPostingState.Published;
// other work here...
}
}
Your Controller can then call the Validator, receive an IValidationResult and pass that back to the presentation layer if needed. Otherwise pass on to Publisher
public ActionResult Publish(PublishViewModel model)
{
var validationResult = _jobService.ValidatePublish(jobPosting);
if(validationResult.Success) _jobService.Publish(jobPosting, validationResult);
else return View("error", validationResult);
}
Edit:
A cleaner solution may be to have the Publisher class return a PublishAttempt result.
public class PublishAttempt : IValidationResult
{
public enum AttemptOutcome {get; set;}
}
public ActionResult Publish(PublishViewModel model)
{
var attempt = _jobService.Publish(jobPosting);
if (attempt.Success) return View("success");
else return View("error", attempt.ValidationResults);
}
The following just came into my mind... what do you think:
I change my service method to:
public IValidationResult Publish(JobPosting jobPosting, bool validateOnly = false){
var validationResult = ValidatePublish(jobPosting);
if(validateOnly) return validationResult;
jobPosting.State = JobPostingState.Published;
...
return validationResult;
}
And then in controller I always call only the Publish method and not the extra ValidatePublish anymore:
public ActionResult Publish(PublishViewModel model)
{
var validationResult = _jobService.Publish(jobPosting);
if(!validationResult.Success) return View("error", validationResult);
}
And when I need only simple validation I do
var validationResult = _jobService.Publish(jobPosting, true);
Is this okey to do it like that?
Or is it not good looking if a normal service call returns IValidationResult?
I'm using MVC 2 with EF4. Creating or deleting objects works but update doesn't.
I have read lot and lot tutorials / questions on StackOverflow, but I haven't found a real WORKING code to use in my "Edit" method.
[HttpPost]
public ActionResult Edit(int id, Account model)
{
try
{
Account accountEdited = accountRepository.Get(id);
// Working code to update "accountEdited" with "model"'s values ???
accountRepository.Save();
return RedirectToAction("Details", new { id = id });
}
catch (Exception ex)
{
return View();
}
}
I'm using EntityFramework with WCF Data Service
This is what I do:
[HttpPost]
public ActionResult Edit(int id, Account model)
{
try
{
Account accountEdited = accountRepository.Get(id);
TryUpdateModel(accountEdited);
ctx.SaveChanges();
return RedirectToAction("Details", new { id = id });
}
catch (Exception ex)
{
return View();
}
}
The initial call to the repository will ensure the entity is in the graph, in an Unmodified state. MVC's built in TryUpdateModel method will then merge the two objects together (the accountEdited object, and the form post data that has been model bound).
This will result in the entity being in a Modified state.
Simply calling SaveChanges() on the object context will then push the changes to the database.
I have dabbled in techniques such as the "stub technique" but it introduced a world of pain (mainly to do with relationships), so this is the easiest approach, and works well.
You have your real EF work abstracted from you code you posted. Here is the simplest EF save:
public void Save(Account account)
{
using (DBContext ctx= new DBContext ())
{
ctx.Attach(account);
ctx.SaveChanges();
}
}
You only need to attach the object if it was obtained in another context. If not you can do it like:
public void Save(int AccountID)
{
using (DBContext ctx= new DBContext ())
{
Account account = ctx.Account.Single(a => a.ID == AccountID)
account.property = somepropchange;
ctx.SaveChanges();
}
}
I don't know if this is the right way of doing this or not, but I am using Jquery and MVC2. I am using a the $.ajax method to make a call back to a controller to do some business logic on a .blur of a textbox.
I have two views that basically do the same thing with the common data, but are using different models. They both use the same controller. It might be easier to explain with code:
So here are the two models:
public class RecordModel {
public string RecordID { get; set; }
public string OtherProperties { get; set; }
}
public class SecondaryModel {
public string RecordID { get; set; }
public string OtherPropertiesDifferentThanOtherModel { get; set; }
}
There are two views that are strongly typed to these models. One is RecordModel, the other SecondaryModel.
Now on these views is a input="text" that is created via:
<%= Html.TextBoxFor(model => model.RecordID) %>
There is jQuery javascript that binds the .blur method to a call:
<script>
$('#RecordID').blur(function() {
var data = new Object();
data.RecordID = $('#RecordID').val();
// Any other stuff needed
$.ajax({
url: '/Controller/ValidateRecordID',
type: 'post',
dataType: 'json',
data: data,
success: function(result) {
alert('success: ' + result);
},
error: function(result) {
alert('failed');
}
});
}
</script>
The controller looks like:
[HttpPost]
public ActionResult ValidateRecordID(RecordModel model) {
// TODO: Do some verification code here
return this.Json("Validated.");
}
Now this works fine if I explicitly name the RecordModel in the controller for the View that uses the RecordModel. However, the SecondaryModel view also tries to call this function, and it fails because it's expecting the RecordModel and not the SecondaryModel.
So my question is this. How can two different strongly typed views use the same Action in a controller and still adhering to the modeling pattern? I've tried abstract classes and interfaces (and changing the view pages to use the Interface/abstract class) and it still fails.
Any help? And sorry for the robustness of the post...
Thanks.
You could define an interface for those classes.
interface IRecord
{
string RecordID { get; set; }
string OtherProperties { get; set; }
}
and make the method receive the model by using that:
[HttpPost]
public ActionResult ValidateRecordID(IRecord model)
{
// TODO: Do some verification code here
return this.Json("Validated.");
}
If you only need the RecordID, you can just have the controller method take int RecordID and it will pull that out of the form post data instead of building the view model back up and providing that to your action method.
[HttpPost]
public ActionResult ValidateRecordID(int RecordID) {
// TODO: Do some verification code here
return this.Json("Validated.");
}
There is no direct way of binding data to a interface/abstract class. The DefaultModelBinder will try to instantiate that type, which is (by definition) impossible.
So, IMHO, you should not use that option. And if you still want to share the same controller action between the two views, the usual way of doing that would be using a ViewModel.
Make your strongly-typed views reference that viewmodel. Make the single shared action receive an instance of it. Inside the action, you will decide which "real" model should be used...
If you need some parameter in order to distinguish where the post came from (view 1 or 2), just add that parameter to the ajax call URL.
Of course, another way is keeping what you have already tried (interface/abstract class), but you'll need a custom Model Binder in that case... Sounds like overcoding to me, but it's your choice.
Edit After my dear SO fellow #Charles Boyung made a gracious (and wrong) comment below, I've come to the conclusion that my answer was not exactly accurate. So I have fixed some of the terminology that I've used here - hope it is clearer now.
In the case above your action could accept two strings instead of a concrete type.
Another possibility is having two actions. Each action taking one of your types. I'm assuming that functionality each type is basically the same. Once the values have been extracted hand them off to a method. In your case method will probably be the same for each action.
public ActionResult Method1(Record record)
{
ProcessAction(record.id, record.Property);
}
public ActionResult Action2(OtherRecord record)
{
ProcessAction(record.id, record.OtherProperty);
}
private void ProcessAction(string id, string otherproperity)
{
//make happen
}
I have an several controllers where I want every ActionResult to return the same viewdata. In this case, I know I will always need basic product and employee information.
Right now I've been doing something like this:
public ActionResult ProductBacklog(int id) {
PopulateGlobalData(id);
// do some other things
return View(StrongViewModel);
}
Where PopulateGlobalData() is defined as:
public void PopulateGlobalData(int id) {
ViewData["employeeName"] = employeeRepo.Find(Thread.CurrentPrincipal.Identity.Name).First().FullName;
ViewData["productName"] = productRepo.Find(id).First().Name;
}
This is just pseudo-code so forgive any obvious errors, is there a better way to be doing this? I thought of having my controller inherit a class that pretty much does the same thing you see here, but I didn't see any great advantages to that. It feels like what I'm doing is wrong and unmaintable, what's the best way to go about this?
You could write a custom action filter attribute which will fetch this data and store it in the view model on each action/controller decorated with this attribute.
public class GlobalDataInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string id = filterContext.HttpContext.Request["id"];
// TODO: use the id and fetch data
filterContext.Controller.ViewData["employeeName"] = employeeName;
filterContext.Controller.ViewData["productName"] = productName;
base.OnActionExecuted(filterContext);
}
}
Of course it would much cleaner to use a base view model and strongly typed views:
public class GlobalDataInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string id = filterContext.HttpContext.Request["id"];
// TODO: use the id and fetch data
var model = filterContext.Controller.ViewData.Model as BaseViewModel;
if (model != null)
{
model.EmployeeName = employeeName;
model.ProductName = productName;
}
base.OnActionExecuted(filterContext);
}
}
Now all that's left is to is to decorate your base controller with this attribute:
[GlobalDataInjector]
public abstract class BaseController: Controller
{ }
There's another more interesting solution which I personally prefer and which involves child actions. Here you define a controller which handles the retrieval of this information:
public class GlobalDataController: Index
{
private readonly IEmployeesRepository _employeesRepository;
private readonly IProductsRepository _productsRepository;
public GlobalDataController(
IEmployeesRepository employeesRepository,
IProductsRepository productsRepository
)
{
// usual constructor DI stuff
_employeesRepository = employeesRepository;
_productsRepository = productsRepository;
}
[ChildActionOnly]
public ActionResult Index(int id)
{
var model = new MyViewModel
{
EmployeeName = _employeesRepository.Find(Thread.CurrentPrincipal.Identity.Name).First().FullName,
ProductName = _productsRepository.Find(id).First().Name;
};
return View(model);
}
}
And now all that's left is to include this wherever needed (probably the master page if global):
<%= Html.Action("Index", "GlobalData", new { id = Request["id"] }) %>
or if the id is part of the routes:
<%= Html.Action("Index", "GlobalData",
new { id = ViewContext.RouteData.GetRequiredString("id") }) %>
I thought of having my controller inherit a class that pretty much does the same thing you see here, but I didn't see any great advantages to that.
This is the way to go, in my opinion. You'd create a base Controller class that would provide this functionality. If you are familiar with the ASP.NET WebForms model then this is similar to creating a custom base Page class.
As to the advantages of putting it in a base class, the main advantages are readability, maintainability and reusability. If you copy and paste the above method into each controller that needs it, you are going to have a more difficult time if, down the road, you need to add new information to the ViewData collection.
In short, anytime you catch yourself copying and pasting code among classes or views in your application you should stop and think about how to put such logic in a single place. For more, read up on DRY - Don't Repeat Yourself.
Here's a simplification of my real models in ASP.NET MVC, that I think will help focus in on the problem:
Let's say I have these two domain objects:
public class ObjectA
{
public ObjectB ObjectB;
}
public class ObjectB
{
}
I also have a view that will allow me to create a new ObjectA and that includes selecting one ObjectB from a list of possible ObjectBs.
I have created a new class to decorate ObjectA with this list of possibilities, this is really my view model I guess.
public class ObjectAViewModel
{
public ObjectA ObjectA { get; private set; }
public SelectList PossibleSelectionsForObjectB { get; private set; }
public ObjectAViewModel(ObjectA objectA, IEnumerable<Location> possibleObjectBs)
{
ObjectA = objectA;
PossibleSelectionsForObjectB = new SelectList(possibleObjectBs, ObjectA.ObjectB);
}
}
Now, what is the best way to construct my view and controller to allow a user to select an ObjectB in the view, and then have the controller save ObjectA with that ObjectB selection (ObjectB already exists and is saved)?
I tried creating a strongly-typed view of type, ObjectAViewModel, and binding a Html.DropDownList to the Model.PossibleSelectionsForObjectB. This is fine, and the I can select the object just fine. But getting it back to the controller is where I am struggling.
Attempted solution 1:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ObjectAViewModel objectAViewModel)
This problem here is that the objectAViewModel.ObjectA.ObjectB property is null. I was thinking the DropDownList which is bound to this property, would update the model when the user selected this in the view, but it's not for some reason.
Attempted solution 2:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ObjectA objectA)
This problem here is that the ObjectA.ObjectB property is null. Again, I thought maybe the DropDownList selection would update this.
I have also tried using the UpdateModel method in each of the above solutions, with no luck. Does anyone have any ideas? I'm guessing I'm missing a binding or something somewhere...
Thanks!
I use code as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude = "Id")]ObjectA objectAToCreate)
{
try
{
Repository.AddObjectA(objectAToCreate);
return RedirectToAction("Details", new { id = objectAToCreate.Id });
}
catch
{
return View();
}
}
With the following code in a Repository (Entity Framework specific):
public void AddObjectA(ObjectA objectAToAdd)
{
objectAToAdd.ObjectB = GetObjectB(objectAToAdd.ObjectB.Id);
_entities.AddToObjectAs(objectAToAdd);
_entities.SaveChanges();
}
public void GetObjectB(int id)
{
return _entities.ObjectBs.FirstOrDefault(m => m.id == id);
}
As per your commments, it is essentially reloading the object from the underlying data service, however I didn't find the need to use the ModelState to access the attempted value.
This is based on a view coded along these lines:
<p>
<%= Html.LabelFor( f => f.ObjectB.Id) %>
<%= Html.DropDownList("ObjectB.Id", new SelectList((IEnumerable)ViewData["ObjectBList"], "Id", "Descriptor"),"") %>
<%= Html.ValidationFor( f => f.ObjectB, "*") %>
</p>
Note that this could be improved to use a strongly typed ViewModel (which I believe you already do) and also to create a custom Editor Template for ObjectB such that the call could be made using:
<%= Html.EditorFor( f => f.ObjectB ) %>
After some more research it doesn't look like this is a case ASP.NET MVC will take care of for me. Perhaps there is a data service binding model I can use (so MVC would automatically grab the appropriate object out of memory, based on what was selected in the dropdown), but for now, I can fix this by handling it in the controller:
Get the selected item from the dropdown using Controller.ModelState
Reload that ObjectB from the underlying data service
Assign that ObjectB to ObjectA.ObjectB
Save ObjectA
So my controller method looks like this now:
Edited based on the comment from LukLed
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ObjectA objectA, string objectBStr)
{
ObjectB objectB = _objBService.Get(objectBStr);
objectA.ObjectB = objectB;
_objAService.Save(objectA);
return RedirectToAction("Details", new { id = objectA.Id });
}