How to update database by attaching model to DataContext - asp.net-mvc-3

I am building a web site using MVC 3 Framework.I have a Linq to SQL DataContext to handle data access.According to what I've learned you can use a post method to get the form information when a user submits a form and MVC frmaework can map the data to a model object.
something like this :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Entity model)
But I have problem with attaching this model object to the data context object here's what I am doing :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Entity model)
{
if(ModelState.IsValid)
{
_dataContext.Attach(model);
_dataContext.SubmitChanges();
return RedirectToAction("Index");
}
return View("Edit", model);
}
But nothing happens.What am I doing wrong ?

The problem is that your _dataContext doesn't know that your entity has been changed.
So you would have to use the overload that sets modified as true:
_dataContext.Attach(model, true);

Related

Required ModelValidation just for new objects ASP.NET MVC

I have this issue since yesterday.
In my User model I have a [NotMapped] called "ConfirmPassword". I don´t save it on the database but I use it on my Create form as a always to validate the data input for new users.
Since than, it´s ok. The problem is on my [HttpPost] Edit action. I should be able to edit some user's data without type and confirm the password. I use both Password and ConfirmPassword as a way to confirm the old password and informe the new one, if I wanna change the password. But, if I don´t, I leave them blank.
I have used already the code below to be able to pass the ModelState.IsValid() condition and it worked:
ModelState["Password"].Errors.Clear();
ModelState["ConfirmPassword"].Errors.Clear();
But, just before the db.SaveChanges(), as User user view model is considered, it has both properties empty and I got:
Property: ConfirmPassword Error: The field ConfirmPassword is invalid.
The question is: How could I skip de Required model validation when I want to update an object?
I read already about custom ModelValidations with classes extending ValidationAttribute and
DataAnnotationsModelValidator but I am not doing it right.
Any idea? How could I create a custom model validation that checks if the UserId property is null or not. It´s a nice way to check if I'm in Create or Edit action.
Thanks,
Paulo
Using the domain objects as your ViewModel will leads you to a condition of less scalability. I would opt for seperate ViewModels specific for the Views. When i have to save the data i map the ViewModel to the Domain model and save that. In your speciific case, i would create 2 ViewModels
public class CustomerViewModel
{
public string FirstName { set;get;}
public string LastName { set;get;}
}
And i will Have another ViewModel which inherits from the above class, for the Create View
public class CustomerCreateViewModel :CustomerViewModel
{
[Required]
public string Password { set;get;}
[Required]
public string ConfirmPassword { set;get;}
}
Now in my Get actions, i use this ViewModel
public ActionResult Create()
{
var vm=new CustomerCreateViewModel();
return View(vm);
}
and of course my View(create.cshtml) is now binded to this ViewModel
#model CustomerCreateViewModel
<h2>Create Csustomer</h2/>
//Other form stuff
Similarly for My Edit Action,
public ActionResult Edit(int id)
{
var vm=new CustomerViewModel();
var domainCustomer=repo.GetCustomerFromID(id);
if(domainCustomer!=null)
{
//This manual mapping can be replaced by AutoMapper.
vm.FirstName=domainCustomer.FirstName;
vm.LastName=domainCustomer.LastName;
}
return View(vm);
}
This view is bounded to CustomerViewModel
#model CustomerViewModel
<h2>Edit Info of #Model.FirstName</h2>
//Other form stuff
In your POST Actions, Map it back to the Domain object and Save
[HttpPost]
public ActionResult Create(CustomerCreateViewModel model)
{
if(ModelState.IsValid)
{
var domainCust=new Customer();
domainCust.FirstName=model.FirstName;
repo.InsertCustomer(domainCust);
//Redirect if success (to follow PRG pattern)
}
return View(model);
}
Instead of writing the Mapping yourself, you may consider using AutoMapper library to do it for you.

Passing model from view to controller

After Learning Jon Galloways MVC Music Store Example.I Just didn't understood the create view How to pass model to controller in which we could see it from parameter in the action Create(Movie movie). Thanks.
[HttpPost]
public ActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie);//Where is the movie come from?
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
In the code example you have posted, the Movie model will be created through model binding. During this process any of your form variables will be matched up with the object specified in the action.
For instance the value of
<input type="text" name="Title"/>
would be assigned to movie's Title property.
A view can be associated with a model by declaring (Razor syntax)
#model GallowaySample.Movie
Normally you do not pass the model to the controller, but you create an instance of your model in your controller.

ViewBag, ViewData and TempData

Could any body explain, when to use
TempData
ViewBag
ViewData
I have a requirement, where I need to set a value in a controller one, that controller will redirect to Controller Two and Controller Two will render the View.
I have tried to use ViewBag, the value gets lost by the time I reach Controller Two.
Can I know when to use and advantages or disadvantages?
Thanks
1)TempData
Allows you to store data that will survive for a redirect. Internally it uses the Session as backing store, after the redirect is made the data is automatically evicted. The pattern is the following:
public ActionResult Foo()
{
// store something into the tempdata that will be available during a single redirect
TempData["foo"] = "bar";
// you should always redirect if you store something into TempData to
// a controller action that will consume this data
return RedirectToAction("bar");
}
public ActionResult Bar()
{
var foo = TempData["foo"];
...
}
2)ViewBag, ViewData
Allows you to store data in a controller action that will be used in the corresponding view. This assumes that the action returns a view and doesn't redirect. Lives only during the current request.
The pattern is the following:
public ActionResult Foo()
{
ViewBag.Foo = "bar";
return View();
}
and in the view:
#ViewBag.Foo
or with ViewData:
public ActionResult Foo()
{
ViewData["Foo"] = "bar";
return View();
}
and in the view:
#ViewData["Foo"]
ViewBag is just a dynamic wrapper around ViewData and exists only in ASP.NET MVC 3.
This being said, none of those two constructs should ever be used. You should use view models and strongly typed views. So the correct pattern is the following:
View model:
public class MyViewModel
{
public string Foo { get; set; }
}
Action:
public Action Foo()
{
var model = new MyViewModel { Foo = "bar" };
return View(model);
}
Strongly typed view:
#model MyViewModel
#Model.Foo
After this brief introduction let's answer your question:
My requirement is I want to set a value in a controller one, that
controller will redirect to ControllerTwo and Controller2 will render
the View.
public class OneController: Controller
{
public ActionResult Index()
{
TempData["foo"] = "bar";
return RedirectToAction("index", "two");
}
}
public class TwoController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Foo = TempData["foo"] as string
};
return View(model);
}
}
and the corresponding view (~/Views/Two/Index.cshtml):
#model MyViewModel
#Html.DisplayFor(x => x.Foo)
There are drawbacks of using TempData as well: if the user hits F5 on the target page the data will be lost.
Personally I don't use TempData neither. It's because internally it uses Session and I disable session in my applications. I prefer a more RESTful way to achieve this. Which is: in the first controller action that performs the redirect store the object in your data store and user the generated unique id when redirecting. Then on the target action use this id to fetch back the initially stored object:
public class OneController: Controller
{
public ActionResult Index()
{
var id = Repository.SaveData("foo");
return RedirectToAction("index", "two", new { id = id });
}
}
public class TwoController: Controller
{
public ActionResult Index(string id)
{
var model = new MyViewModel
{
Foo = Repository.GetData(id)
};
return View(model);
}
}
The view stays the same.
TempData
Basically it's like a DataReader, once read, data will be lost.
Check this Video
Example
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
String str = TempData["T"]; //Output - T
return View();
}
}
If you pay attention to the above code, RedirectToAction has no impact over the TempData until TempData is read. So, once TempData is read, values will be lost.
How can i keep the TempData after reading?
Check the output in Action Method Test 1 and Test 2
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
string Str = Convert.ToString(TempData["T"]);
TempData.Keep(); // Keep TempData
return RedirectToAction("Test2");
}
public ActionResult Test2()
{
string Str = Convert.ToString(TempData["T"]); //OutPut - T
return View();
}
}
If you pay attention to the above code, data is not lost after RedirectToAction as well as after Reading the Data and the reason is, We are using TempData.Keep(). is that
In this way you can make it persist as long as you wish in other controllers also.
ViewBag/ViewData
The Data will persist to the corresponding View
ViewBag, ViewData, TempData and View State in MVC
http://royalarun.blogspot.in/2013/08/viewbag-viewdata-tempdata-and-view.html
ASP.NET MVC offers us three options ViewData, VieBag and TempData for passing data from controller to view and in next request. ViewData and ViewBag are almost similar and TempData performs additional responsibility.
Similarities between ViewBag & ViewData :
Helps to maintain data when you move from controller to view. Used to
pass data from controller to corresponding view. Short life means
value becomes null when redirection occurs. This is because their goal
is to provide a way to communicate between controllers and views. It’s
a communication mechanism within the server call.
Difference between ViewBag & ViewData:
ViewData is a dictionary of objects that is derived from
ViewDataDictionary class and accessible using strings as keys. ViewBag
is a dynamic property that takes advantage of the new dynamic features
in C# 4.0. ViewData requires typecasting for complex data type and
check for null values to avoid error. ViewBag doesn’t require
typecasting for complex data type.
ViewBag & ViewData Example:
public ActionResult Index()
{
ViewBag.Name = "Arun Prakash";
return View();
}
public ActionResult Index()
{
ViewData["Name"] = "Arun Prakash";
return View();
}
In View, we call like below:
#ViewBag.Name
#ViewData["Name"]
TempData:
Helps to maintain data when you move from one controller to other
controller or from one action to other action. In other words when you
redirect, “Tempdata” helps to maintain data between those redirects.
It internally uses session variables. TempData is meant to be a very
short-lived instance, and you should only use it during the current
and the subsequent requests only
The only scenario where using TempData will reliably work is when you are redirecting. This is because a redirect kills the current request (and sends HTTP status code 302 Object Moved to the client), then creates a new request on the server to serve the redirected view.
It requires typecasting for complex data type and check for null values to avoid error.
public ActionResult Index()
{
var model = new Review()
{
Body = "Start",
Rating=5
};
TempData["ModelName"] = model;
return RedirectToAction("About");
}
public ActionResult About()
{
var model= TempData["ModelName"];
return View(model);
}
void Keep()
Calling this method with in the current action ensures that all the items in TempData are not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep(); // retains all strings values
}
void Keep(string key)
Calling this method with in the current action ensures that specific item in TempData is not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep("emp"); // retains only "emp" string values
}
TempData
will be always available until first read, once you read it its not available any more can be useful to pass quick message also to view that will be gone after first read.
ViewBag
Its more useful when passing quickly piece of data to the view, normally you should pass all data to the view through model , but there is cases when you model coming direct from class that is map into database like entity framework
in that case you don't what to change you model to pass a new piece of data, you can stick that into the viewbag
ViewData is just indexed version of ViewBag and was used before MVC3
Also the scope is different between viewbag and temptdata. viewbag is based on first view (not shared between action methods) but temptdata can be shared between an action method and just one another.

Pass viewmodel to ajax controller action that returns partial with that model?

I have a view that is strongly typed. Inside this view i have jqueryui tabs, that when clicked call my Controller and return a partial view
("#tab0").load('#Url.Action("ProfileImage", "User")');
public ActionResult ProfileImage()
{
return PartialView("_ProfileImage");
}
What I'd like to do is pass the model from the "parent" view to the controller which can then bind it to the partial when it is returned:
("#tab0").load('#Url.Action("ProfileImage", "User", new {model=model})');
public ActionResult ProfileImage(UserViewModel model)
{
return PartialView("_ProfileImage", model);
}
Is this possible? how is this normally done? Where you have the model data in one view and you'd like to pass it to a asynchronously loaded partial view?
You could create a ToJson method on your viewmodel, which could be something like this:
public IHtmlString ToJson()
{
return MvcHtmlString.Create(Json.Encode(this));
}
It just serializes the viewmodel into a json. The IHtmlString returntype makes sure the output isn't encoded in your view.
The call to your controller would be something like this:
("#tab0").load('#Url.Action("ProfileImage", "User", new {model=model.ToJson()})');
The json modelbinder can recreate the viewmodel on the serverside. You probably run into some problems along the way, but nothing unsolvable I guess.

Access HiddenFor(Model=>Model.Id) MVC ASP.NET in Controller

How do I access the following written in View
#Html.HiddenFor(Model=>Model.id)
in Controller
?
Thank you
Create a Action method that takes the Type that you used as Model in your view.
[HttpPost]
public ActionResult Create(MyModel model)
{
var id = model.id;//the id in #Html.HiddenFor(Model=>Model.id)
}

Resources