I have created a asp.net mvc application which has Get and Post method like
public ActionResult MyData(string companyid)
{
MyModel model= new MyModel ();
model = (LINQ) ;
return View(model);
}
[HttpPost]
public ActionResult MyData(MyModel model)
{
if(model.companyid>0)
{
//Update Database
}
else
{
// insert database
}
return View(model);
}
public class MyModel
{
public int companyid {set; get;}
public string Name {set; get;}
public string Address {set; get;}
}
Now my question is: I am getting model.companyid =0 always in post i.e. # if(model.companyid>0)
when I update the get method
from public ActionResult MyData(string companyid)
to public ActionResult MyData(string cid)
it works.
i.e. rename companyid to cid, why it happens. Please let me know...I think, we have seperate method for get and post.
Related
I'm looking for a way to have more than one action method with the same name in controller without changing Url (route).
[HTTPPost]
Public ActionResult Method1 (Dto1 param)
{
}
[HTTPPost]
Public ActionResult Method2 (Dto2 param)
{
}
[HTTPPost]
Public ActionResult Method3 (Dto3 param)
{
}
This throws error -
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints
Dto1, Dto2 and Dto3 derive from a base Dto, each have properties specific to different request methods. I am trying to avoid having a single method with a common Dto which will require multiple validations such as validating mandatory fields based on the value of other fields, etc. If we can have 3 different Post methods with different Dtos, it would makes things much easier
Adding Dtos (Simplified)
public class BaseDto
{
public string CommonProp1 { get; set; }
public string CommonProp2 { get; set; }
}
public class Dto1: BaseDto
{
public enumType Type = enumType.Type1;
public string Property1 { get; set; }
}
public class Dto2 : BaseDto
{
public enumType Type = enumType.Type2;
public string Property2 { get; set; }
}
public class Dto3 : BaseDto
{
public enumType Type = enumType.Type3;
public string Property3 { get; set; }
}
You can use Routes or calling a private method from the three above methods, you shouldn't do this as you want. I think your problem is more deep.
But.... if you still want it, here is a workaround.
Instead of receiving an object, receive a string with json content and parse the object.
But you will have to have a property inside the "json object" or another parameter that defines you wich object it is (Dto1, Dto2 or Dto3). In any case will be the same that use different routes or methods because objects are different.
[HTTPPost]
Public ActionResult Method (string param)
{
//Decode your string called param with JSON with a property inside
}
or
[HTTPPost]
Public ActionResult Method (string param, int type)
{
//Decode your string called param with JSON switching "type" as 1, 2 or 3
}
UPDATE after your update:
I suggest you receive BaseDto and the type in other parameter.
[HTTPPost]
Public ActionResult Method (BaseDto param, int type)
{
}
I have a checkout page that has multiple methods for payment. Each method has its own partial view containing its own model. I am trying to get keep the url the same on each of the different methods so if there is an error the url doesn't change. Is there a way to achieve this? Thank you for any help, I have been mulling over this for a while now.
CheckOut Model
public class CheckoutForm
{
public Method1Form method1Form { get; set; }
public Method2Form method2Form { get; set; }
public Method3Form method3Form { get; set; }
}
CheckOut Controller
[HttpGet]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid)
{
....
return View(model);
}
[HttpPost]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid, Method1 model)
{
....
//Some Error Condition Triggered
return View(checkoutmodel);
}
[HttpPost]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid, Method2 model)
{
....
//Some Error Condition Triggered
return View(checkoutmodel);
}
[HttpPost]
[Route("checkout/{guid}")]
public IActionResult Checkout([FromRoute] String guid, Method3 model)
{
....
//Some Error Condition Triggered
return View(checkoutmodel);
}
Similar Question without an answer https://stackoverflow.com/questions/42644136
You cannot. There is no way for Route Engine to differentiate those 3 post methods.
You could append something at the end to the URL to make them different.
[HttpPost]
[Route("checkout/{guid}/paypal")]
public IActionResult Checkout([FromRoute] String guid, Method1 model)
{
....
}
[HttpPost]
[Route("checkout/{guid}/authorizenet")]
public IActionResult Checkout([FromRoute] String guid, Method2 model)
{
....
}
I've got a model that represents a joint table (with payload) in my database:
public class UserHasCar
{
// Foreign keys
[Key, Column(Order = 0)]
public string ApplicationUserId { get; set; }
[Key, Column(Order = 1)]
public int CarId { get; set; }
// Navigation properties
[Required]
public virtual ApplicationUser ApplicationUser { get; set; }
[Required]
public virtual Car Car{ get; set; }
// Additional fields
public int YearsRidden { get; set; }
}
public class Car
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
public class ApplicationUser : IdentityUser
{
public int BirthYear{ get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
I have a form that includes multiple select boxes, and upon submitting I want to clear out all records related to that user who submitted the form in the UserHasCar table and replace them with the new updated information. I'm getting a An entity object cannot be referenced by multiple instances of IEntityChangeTracker. because I am doing something wrong, but I don't see where I am using more than one context. This code happens in my controller:
public ApplicationUser GetCurrentUser()
{
return UserManager.FindById(User.Identity.GetUserId());
}
public string GetUserId()
{
string id = User.Identity.GetUserId();
var user = UserManager.FindById(id);
return user.Id;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManageCars(FormCollection form)
{
string id = GetUserId();
// Remove cars records with my id from database
var queryCars = (from m in db.UserHasCars where m.ApplicationUserId == id select m).ToList();
foreach (var record in queryCars )
{
// Could the problem be here?
db.UserHasCars.Remove(record)
}
// Add user-submitted cars to the database
string carval = form["Cars[0]"];
Car car = (from m in db.Cars where m.Name == carval select m).First();
int carid = car.ID;
// I get the abovementioned title error here
db.UserHasCars.Add(
new UserHasCar()
{
ApplicationUser = GetCurrentUser(),
ApplicationUserId = id,
Car = car,
CarId = carid,
YearsRidden = 0
}
);
db.SaveChanges();
}
I've seen many SO posts, but can't seem the problem as why my code doesn't want to save the new database entries.
EDIT
The solution was to remove the call to get the user and replace it with a query. Why? I was making database conflict errors by having both types of calls (database and DataManager calls in the same controller action). I ended up using a modified GetUser() function instead of GetCurrentUser()
Code:
public ApplicationUser GetUser()
{
// As opposed to:
// UserManager.FindById(User.Identity.GetUserId())
// We make a database call to grab our user instead
// So we don't get database context conflicts by using UserManager
string id = GetUserId();
return db.Users.Where(m => m.Id == id).First();
}
public string GetUserId()
{
return User.Identity.GetUserId();
}
// snip
// in ManageCars(FormCollection form)
ApplicationUser user = GetUser();
// snip
var newRow = db.UserHasCars.Create();
newRow.ApplicationUser = user;
// snip
db.UserHasCars.Add(newRow);
Try removing this line:
ApplicationUser = GetCurrentUser(),
from your object instantiation when adding.
Entity populates this object automatically once you set the foreign key ApplicationUserId. If UserManager.FindById(User.Identity.GetUserId()) uses a different db context that's where your exception is coming from.
Also to save yourself further trouble down the line, you should always call db.SaveChanges() in between the two operations. If you're worried about the atomicity of the db operation, just wrap the whole thing in a Transaction.
And when adding new rows to a table, I usually prefer to use something like:
var newRow = db.SomeTable.Create();
newRow.SomeColumn1 = "something";
newRow.SomeColumn2 = 5;
db.SomeTable.Add(newRow);
db.SaveChanges();
In order to delete entries from UserHasCars you need to change their EntityState to Deleted.
Example:
foreach (var record in queryCars )
{
db.ObjectStateManager.ChangeObjectState(record, EntityState.Deleted);
}
Hope this will fix your issue.
I am creating an MVC4 application that has an index page that shows a grid. The index action for the page has parameters for a search string, sort order, account id, and vendor id that control what is shown.
public class MyController
{
public ActionResult Index(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
...
}
}
From this main page I can click on rows in the grid, which take me to the create and edit actions. In addition, I have several other actions on the control that are called from the index page.
When I press Save or Cancel on any of these views (the [HttpPost] action, as usual they redirect to the index action. But these views don't have the search string, sort order, account id, and vendor id so that the index view can "restore" what was being shown.
So I have been adding these parameters to the other actions methods, and making an object with the properties needed to in the view models for the methods.
public class PageFeatures
{
public string SearchFilter {get; set;}
public string SortOrder {get; set;}
public int AccountId {get; set;}
public int VendorId {get; set;}
}
public class CreateViewModel
{
public PageFeatures Features { get; set; }
}
public class EditViewModel
{
public EditViewModel(int id)
{
Id=id;
}
public int Id {get; set;}
public PageFeatures Features { get; set; }
}
public class MyController
{
public ActionResult Index(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
...
}
public ActionResult Create(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
var model = new CreateViewModel();
model.Features = new PageFeatures
{
SearchFilter = searchFilter,
SortOrder = sortOrder,
AccountId = accountid,
VendorId = vendorid
}
return View(model);
}
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
public ActionResult Edit( int id=0,
string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
var model = new EditViewModel(id);
model.Features = new PageFeatures
{
SearchFilter = searchFilter,
SortOrder = sortOrder,
AccountId = accountid,
VendorId = vendorid
}
return View(model);
}
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
}
But what I don't like about this approach is that I am peppering my views and every controller method with these values.
Am I doing the right thing, or is there a better way, like an action filter that would make this stuff easier and less repetitive?
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
You are using the right approach.
If you dont want your URL to be ugly filled with query string parameters. You can try storing these values in a session and retrieving them in each action of your controller. Set them once and your done to use it anywhere, if you want to change the session values you can just over write it
Have you thought about using TempData and setting these params in there. The best part is that you can persist TempData across Controllers and you can perhaps modify the TempData values in a base controller class.
I am trying to find the best way to use MVC for models which are only partially edited.
Below is an simple example.
Model
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
View
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
Controller
using System.Web.Mvc;
public class SimpleController : Controller
{
public ActionResult Edit(int id)
{ return View(Get(id)); }
[HttpPost]
public ActionResult Edit(int id, SimpleModel model)
{
if (model.Name.StartsWith("Child")) //Some test that is not done client-side.
{
Save(model);
//Get the saved data freshly.
//model = Get(id);
}
else
{
ModelState.AddModelError("", "Name should start with 'Child'");
}
//Is this the way to set the Parent property?
//var savedModel = Get(id);
//model.Parent = savedModel.Parent;
return View(model);
}
//Mock a database.
SimpleModel savedModel;
private void Save(SimpleModel model)
{ savedModel = new SimpleModel() { Id = model.Id, Name = model.Name }; }
private SimpleModel Get(int id)
{
if (savedModel == null)
{ return new SimpleModel() { Id = id, Parent = "Father", Name = "Child " + id.ToString() }; }
else
{ return new SimpleModel() { Id = savedModel.Id, Parent = "Father", Name = savedModel.Name }; }
}
}
The Name field is editable. The Parent field is only for reference and should not be updated. Therefore, it is rendered using DisplayFor.
Upon post, I receive a model with property Parent set to null. That's no problem as it will not be saved. However, when I simply return the received model to the view, the Parent field will no longer be displayed. When the model is valid, I can easily get it again from the database and thus get the Parent field's value back.
When the model is not valid, I would like to allow the user to correct input and attempt to save once more. There, I the received model's values that are input should be used, but the displayed values should be shown as well.
In reality, there are many more fields to be shown for reference, most often from different database entities than the one that is being edited.
I have seen suggestions to pass the fields as hidden fields in the view, but I feel very reluctant to read data from the client that should not be updated.
Is there a more elegant way to do this than copying these values into the model manually or passing them as hidden fields?
What about giving those un-editable properties to another model and let it take care of those properties?
View Model
public class PersonViewModel
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public PersonDetailsModel DetailsModel { get; set; }
}
Details Model
public class PersonDetailsModel
{
public string Mother { get; set; }
public string Father { get; set; }
public PersonDetailsModel() { }
public PersonDetailsModel(int personId)
{
// pull required model data from databases
var db = DBParentContext;
Mother = db.Parent.Where(m => m.ChildId == personId)
Father = db.Parent.Where(m => m.ChildId == personId)
}
}
Controller
public class PersonController : Controller
{
[HttpPost]
public ActionResult Edit(PersonViewModel viewModel)
{
viewModel.DetailsModel = new PersonDetailsModel(viewModel.Id)
if (ModelState.IsValid) {
// ~
}
return View(viewModel)
}
}
View
#model PersonViewModel
#Html.DisplayFor(m => m.DetailsModel.Mother)
#Html.DisplayFor(m => m.DetailsModel.Father)
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
Since details like your Mother are un-editable then they're not really part of the "Edit" model, so I'd box like that away and try to let something else take care of them.
If you aren't going to update the Parent field, then it really doesn't matter if it's a hidden or not, since you won't update it on post.
I would use the hidden in this case, just make sure not to update that field.