I want to update existing Product objects in database by images, but image goes to DB successfully only when i create new objects.
I'm trying to update my object this way
[HttpPost]
public ActionResult Edit(Product product, HttpPostedFileBase image)
{
if (ModelState.IsValid)
{
if (image != null)
{
product.ImageMimeType = image.ContentType;
product.ImageData = new byte[image.ContentLength];
image.InputStream.Read(product.ImageData, 0, image.ContentLength);
}
if (product.ProductID != 0)
UpdateModel<Product>(repository.Products.FirstOrDefault(p => p.ProductID == product.ProductID));
repository.SaveProduct(product);
TempData["message"] = string.Format("{0} has been saved", product.Name);
return RedirectToAction("Index");
}
return View(product);
}
//repository.SaveProduct()
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
context.SaveChanges();
}
The View
#
Upload new image: input type="file" name="Image"
input type="submit" value="Save"
#Html.ActionLink("Cancel and return to List", "Index")
}
I noticed you were read the "Pro ASP.NET MVC 3 Framework" and meet this issue same to me.
The author had a error at here, the code should be(You must reference and using the System.Data.Entity namespace at first):
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
else
{
context.Entry(product).State = System.Data.EntityState.Modified;
}
context.SaveChanges();
}
This is all kinds of wrong.
You should be using specific ViewModels for your Edit and Create actions.
Define a separate class containing the properties you wish to edit and any UI validation:
public class EditProductViewModel {
[HiddenInput]
public int Id {get;set;}
[Required]
public string Name {get;set;}
[Required]
public string Description {get;set;}
public HttpPostedFileBase Image {get;set;}
}
Then change your action method like so:
[HttpPost]
public ActionResult Edit(EditProductViewModel viewModel) {
if (ModelState.IsValid) {
var product = repository.Products.FirstOrDefault(p => p.Id == viewModel.Id);
// TODO - null check of product
// now lefty righty
product.Name = viewModel.Name;
product.Description = viewModel.Description;
if (viewModel.Image.ContentLength > 0) {
product.ImageMimeType = image.ContentType; // wouldn't trust this (better to lookup based on file extension)
product.ImageData = new byte[image.ContentLength];
image.InputStream.Read(product.ImageData, 0, image.ContentLength);
}
repository.SaveProduct(product);
return RedirectToAction("Index");
}
return View(viewModel);
}
Here's a good post discussing the ViewModel pattern.
Try doing this
context.Products.Attach(product);
Note: only when doing the update, not when inserting a new product.
Try this:
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
else // Update operation
{
context.Products.Attach(product);
}
context.SaveChanges();
}
Note: I would change the way you determine which is it a new or an updated product.
[HttpPost]
public RedirectToRouteResult Save(TestViewModel viewModel)
{
TempData["Output"] = "Here is some response";
return RedirectToAction("Index", viewModel);
}
Related
From my two models Student and InventoryRecord. I created a ViewModel named TestViewModel. I'm confused as to how do I write my controller?
public class TestViewModel
{
//from Student model
[Key]
public string UserID { get; set; }
public string PhoneNumber{ get; set; }
public string Address{ get; set; }
//other properties
//from Inventory model
public string FathersName { get; set; }
public string FathersAddress { get; set; }
//other properties
}
When I'm using only my main model Student. This is how I write my controller:
// GET: Students/CreateEdit
public ActionResult InventoryRecord()
{
var currentUserId = User.Identity.GetUserId();
var newid = db.Students.FirstOrDefault(d => d.UserID == currentUserId);
if (newid == null)
{
newid = db.Students.Create();
newid.UserID = currentUserId;
db.Students.Add(newid);
}
Student student = db.Students.Find(newid.UserID);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Students/CreateEdit
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult InventoryRecord(Student student)
{
var currentUserId = User.Identity.GetUserId();
var userName = User.Identity.GetUserName();
var u = db.Students.FirstOrDefault(d => d.UserID == currentUserId);
if (u == null)
{
u = db.Students.Create();
u.UserID = currentUserId;
db.Students.Add(u);
}
if (ModelState.IsValid)
{
u.PhoneNumber = student.PhoneNumber;
u.Address = student.Address;
//other properties
db.SaveChanges();
TempData["Message"] = "User: " + userName + ", details successfully updated!";
}
return View(student);
}
Now, I'm really confused how to proceed here. How should I write my controller if I'm using my TestViewModel? Someone please point me in the right direction. Thank you.
Well personally I would move the code out of the controller.
However for example you just need to create an instance of your TestViewModel and pass that to your view. You may also need to update your View if you specific the model in the cshtml.
public ActionResult InventoryRecord()
{
var currentUserId = User.Identity.GetUserId();
var newid = db.Students.FirstOrDefault(d => d.UserID == currentUserId);
if (newid == null)
{
newid = db.Students.Create();
newid.UserID = currentUserId;
db.Students.Add(newid);
}
Student student = db.Students.Find(newid.UserID);
if (student == null)
{
return HttpNotFound();
}
TestViewModel model = new TestViewModel
{
UserID = student.UserId,
PhoneNumber = student.PhoneNumber,
//add the rest.
};
return View(model);
}
Rather than returning Student , return TestViewModel
Student student = db.Students.Find(newid.UserID);
if (student == null)
{
return HttpNotFound();
}
TestViewModel tvm = new TestViewModel()
{
UserID =student.Id,
PhoneNumber = student.PhoneNumber,
Address= student.Address
};
return View(tvm);
}
and second method will be
public ActionResult InventoryRecord(TestViewModel tvm)
I am developing MVC application.
I am trying To Pass cityid to _CreateCityWiseArea action method from CityWiseAreaList in same controller.
But data doesn't pass properly.
_CreateCityWiseArea action method getting null value.
Please check below code...
This Is My Area Controller:
public ActionResult CityWiseAreaList(int cityid)
{
if (cityid == 0)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var data = db.Areas.Where(x => x.CityID == cityid).ToList();
//TempData["ID"] = cityid;
RedirectToAction("_CreateCityWiseArea", new RouteValueDictionary(
new { controller = "Area", action = "_CreateCityWiseArea", id = cityid }));
return View(data);
}
public ActionResult _CreateCityWiseArea(int? id)
{
// var cityid = Convert.ToInt32(TempData["ID"]);
if (id == 0)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Name = db.Cities.Where(c => c.CityID == id).Select(x => x.CityName).FirstOrDefault();
ViewBag.message = Name;
return View();
}
//Add Area to Corresponding city
// POST: Area/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult _CreateCityWiseArea(Area area)
{
if (ModelState.IsValid)
{
db.Areas.Add(area);
db.SaveChanges();
return RedirectToAction("CityWiseAreaList");
}
ViewBag.CityID = new SelectList(db.Cities, "CityID", "CityName", area.CityID);
return PartialView(" ~/Views/Area/_CreateCityWiseArea.cshtml", area);
}
I Use Redirectoaction, tempdata method to pass Value but both time null value is coming.
You want to pass id from CityWiseAreaList to _CreateCityWiseArea this should be simple, since it's in the same Controller you don't need to add the RouteValueDictionary but it's your choice anyway. The below should work, that's the way I'll go:
return RedirectToAction("_CreateCityWiseArea", new { id = cityid });
I have an Event with info about it on a Display Event View. On that view, I am trying to to make Attend functuality, similar to Liking, but I have little problems. My logic is very simple and I would like to keep it so: on click, it will increase int Attends by 1, but ...
First-I am getting inside the if(events==null)... And second is there a way to make it clickable once per User?
Here is my code:
Model:
[Key]
public int Id { get; set; }
public string EventName { get; set; }
...
public int Attends { get; set; }
Event Create Action:
//POST: Event/Create
[HttpPost]
public ActionResult Create(EventViewModel model)
{
if (ModelState.IsValid)
{
using (var database = new EventSpotDbContext())
{
var events = new Event(model.EventName,...);
...
events.Attends = 0;
database.Events.Add(events);
database.SaveChanges();
return RedirectToAction("Main");
}
}
return View(model);
}
Event Attend Action:
public ActionResult Attend(int? id)
{
using (var database = new EventSpotDbContext())
{
var events = database.Events.FirstOrDefault(a => a.Id == id);
if (events == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
events.Attends += 1;
database.SaveChanges();
}
return RedirectToAction("Details");
}
Event Details Action:
//GET: Event/Details
public ActionResult Details(int? id)
{
using (var database = new EventSpotDbContext())
{
var events = ... .First();
return View(events);
}
}
Details View
#model EventSpot.Models.Event
...
#Html.ActionLink("Attend Event", "Attend", "EventController", new {#id=Model.Id} )
#Model.Attends
#Html.ActionLink("Attend Event", "Attend", "EventController", new {#id=Model.Id}, null )
the isse that he was dealing with {#id=Model.Id} as html attributes not route attributes
In my POST Edit function, I have my viewmodel that contain the game I want to update and list of platformIds that I want to add to the game.
Using this code, I was able to add platforms to my game but can't remove them. I put a breakpoint at the end and definitely see that viewModel.Game.Platforms have only what I selected but it is not updated in my game list.
If I add a few platforms and remove some at the same time. The new platforms get added but none are removed.
public ActionResult Edit(GameViewModel viewModel)
{
if (ModelState.IsValid)
{
List<Platform> platforms = new List<Platform>();
foreach (var id in viewModel.PostedPlatforms.PlatformIds)
{
platforms.Add(db.Platforms.Find(Int32.Parse(id)));
}
db.Games.Attach(viewModel.Game);
viewModel.Game.Platforms = platforms;
db.Entry(viewModel.Game).State = EntityState.Modified;
UpdateModel(viewModel.Game);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel.Game);
}
The model class is
public class Game
{
public int GameId { get; set; }
public string Title { get; set; }
public List<Platform> Platforms { get; set; }
}
public class Platform
{
public int PlatformId { get; set; }
public string Name { get; set; }
public List<Game> Games { get; set; }
}
Using ourmandave's suggestion, I got this code which while does change the platforms selection, it creates a new game entry every time which is inefficient and also increasing the id of the content which mess up bookmarks.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(GameViewModel viewModel)
{
if (ModelState.IsValid)
{
List<Platform> platforms = new List<Platform>();
if(viewModel.PostedPlatforms != null)
{
foreach (var id in viewModel.PostedPlatforms.PlatformIds)
{
platforms.Add(db.Platforms.Find(Int32.Parse(id)));
}
}
db.Games.Remove(db.Games.Find(viewModel.Game.PostId));
db.SaveChanges();
viewModel.Game.Platforms = platforms;
db.Games.Add(viewModel.Game);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel.Game);
}
You could try this...
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(GameViewModel viewModel)
{
if (ModelState.IsValid)
{
List<Platform> selectedPlatforms = viewModel.Select(pl => GetPlatformById(pl.Id)).ToList();
var game = GetGameById(viewModel.Id);
UpdateGamePlatforms(game, selectedPlatforms);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel.Game);
}
private Platform GetPlatformById(int platformId)
{
return db.Platforms.First(pl => pl.Id == platformId);
}
private Game GetGameById(int gameId)
{
return db.Games.First(g => g.Id == gameId);
}
private void UpdateGamePlatforms(Game game, IList<Platform> selectedPlatforms)
{
var gamePlatforms = game.Platforms.ToList();
foreach (var gamePlatform in gamePlatforms)
{
if (selectedPlatforms.Contains(gamePlatform) == false)
{
game.Platforms.Remove(gamePlatform);
}
else
{
selectedPlatforms.Remove(gamePlatform);
}
}
game.Platforms.AddRange(selectedPlatforms);
}
UpdateGamePlatformswill remove platforms from the game which are no longer selected. It will leave the platforms which are still selected and it will also add new platforms to the game which have been selected.
Solution: Using TomJerrum's solution, I can now edit the platform list properly. To update the rest of the properties, I have to map all the property of the game object I'm trying to edit to match the property of my viewModel. Thankfully, there is already a function for that so I only have to add db.Entry(game).CurrentValues.SetValues(viewModel.game);.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(GameViewModel viewModel)
{
if (ModelState.IsValid)
{
List<Platform> selectedPlatforms = new List<Platform>();
if (viewModel.PostedPlatforms != null)
{
int[] platformIds = Array.ConvertAll(viewModel.PostedPlatforms.PlatformIds, p => Convert.ToInt32(p));
selectedPlatforms.AddRange(db.Platforms.Where(item => platformIds.Contains(item.PlatformId)).ToList());
}
var game = GetGameById(viewModel.Id);
UpdateGamePlatforms(game, selectedPlatforms);
db.Entry(game).CurrentValues.SetValues(viewModel.game);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel.Review);
}
private Game GetGameById(int gameId)
{
return db.Games.First(g => g.Id == gameId);
}
private void UpdateGamePlatforms(Game game, IList<Platform> selectedPlatforms)
{
var gamePlatforms = game.Platforms.ToList();
foreach (var gamePlatform in gamePlatforms)
{
if (selectedPlatforms.Contains(gamePlatform) == false)
{
game.Platforms.Remove(gamePlatform);
}
else
{
selectedPlatforms.Remove(gamePlatform);
}
}
game.Platforms.AddRange(selectedPlatforms);
}
Having trouble update users in AD
My Model:
public class UserModel
{
....
[ScaffoldColumn(false)]
[DisplayName("Fødselsdag")]
[DataType(DataType.Date)]
[NotMapped]
public DateTime extensionAttribute1_date
{
get
{
try
{
return DateTime.Parse(extensionAttribute1);
}
catch (Exception e)
{
return new DateTime();
}
}
set { }
}
}
My Controller:
[HttpPost]
public ActionResult Edit(string sAMAccountName, FormCollection collection, UserModel data)
{
if (ModelState.IsValid)
{
var config = new LdapConfiguration();
config.ConfigureFactory("domain.local").AuthenticateAs(new NetworkCredential("xxxx", "xxxxx"));
using (var context = new DirectoryContext(config))
{
var user = context.Query(new UserModel(), "OU=users,OU=xxx,DC=xxx,DC=dk", "User").FirstOrDefault(d => d.sAMAccountName == sAMAccountName);
if (user == null) return RedirectToAction("Index");
user.title = data.title;
user.mobile = data.mobile;
user.homePhone = data.homePhone;
user.streetAddress = data.streetAddress;
user.postalCode = data.postalCode;
user.l = data.l;
user.department = data.department;
user.physicalDeliveryOfficeName = data.physicalDeliveryOfficeName;
user.extensionAttribute1 = data.extensionAttribute1_date.ToLongDateString();
context.Update(user);
}
return RedirectToAction("Index");
}
return View();
}
When i submit to Edit Action i results in an error:
The requested attribute does not exist.
If i remove extensionAttribute1_date from the model i updates fine.
How do i exclude my calculated attributes from the update?
I have other attributes in the model such as Age which is calculated! Is this the wrong procedure for this?
/Michael