Thanks in advance for your help. I cannot move forward creating or editing fields in asp.net mvc using two tables or more. As an example: I´ve been using MVC Music Store example to learn
//
// GET: /StoreManager/Create
public ActionResult Create()
{
var viewModel = new StoreManagerViewModel
{
Album = new Album(),
Genres = storeDB.Genres.ToList(),
Artists = storeDB.Artists.ToList()
};
return View(viewModel);
}
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
//Save Album
storeDB.AddToAlbums(album);
storeDB.SaveChanges();
return RedirectToAction("Index");
}
// Invalid – redisplay with errors
var viewModel = new StoreManagerViewModel
{
Album = album,
Genres = storeDB.Genres.ToList(),
Artists = storeDB.Artists.ToList()
};
return View(viewModel);
}
This example takes into consideration that Album is only one table at the model. My main question resides in the way I have to code in order to include another table with FK to Album. As an example if AlbumVariant table - eg: field albumid- displays a FK to Album Table - eg: field albumid-.
I´ve been trying with
[Authorize]
public ActionResult Create(int? id)
{
var albumCreate= db.Album.Include(AlbumVariant)SingleOrDefault(a => a.AlbumId == id);
var viewModel = new AlbumViewModelCreate()
{
AlbumCreate = albumCreate,
};
}
but with no success. Any Help will be appreciated. brdgs
You have to understand that AlbumVariant table takes AlbumId as a foreign key that means AlbumId has to be generated first! You can try this:
storeDB.AddToAlbums(album);
storeDB.SaveChanges();
AlbumVariant av = new AlbumVariant {
...
AlbumId = album.Id //generates after calling the 1st save changes
}
storeDB.AddToAlbumVariant(av);
storeDB.SaveChanges();
Related
I am building a basic Car Rental Application. The user can view the cars and click the Rent button. After clicking it, I need to return a new View which contains a form, that the user has to complete in order to finish the order. I am having problems passing the Car data as well as the Customer data between the controllers in order to complete the Rent.
On the main page, I have a Rent link under every car. Here is the code:
<div class="col-md-12">
<p>#Html.ActionLink("Rent", "Rent" , new { Id = car.Id})</p>
</div>
Rent method from HomeController
public ActionResult Rent(string id)
{
return RedirectToAction("Create", "Rents");
}
Create method from RentsController
[HttpPost]
public ActionResult Create(string carId, Rent rent)
{
if (!ModelState.IsValid)
return View();
var carToRent = context.Cars.SingleOrDefault(c => c.Id == carId);
if (carToRent == null)
return Content($"Car not found!");
rent.Car = carToRent;
var customer = context.Customers.SingleOrDefault(c => c.UserId == User.Identity.Name);
if (customer == null)
return Content($"Customer not found!");
rent.Customer = customer;
context.Rents.Add(rent);
context.SaveChanges();
return RedirectToAction("Index");
}
I am getting an HTTP 404 Error every time I try to access Rents/Create.
You can simplify what you're attempting to do. Main points to note are the following:
You don't need to link to the Rent action if all it does is
redirect to the Create action- just link to the Create action
directly. There is another overload of ActionLink that will let you specify
the controller (see below).
From what you've posted it doesn't look like the Create action
needs to take in a parameter for Rent rent- this can be created
inside the Create action and simplify the data that you need to
pass from view to controller.
Please see my comments in code for further explanantion:
View:
//call the Create action on the RentsController directly from the view
<div class="col-md-12">
<p>#Html.ActionLink("Rent", "Create", "Rents" , new { Id = car.Id }, null)</p>
</div>
Controller:
//modify signature to remove passing a Rent object it
//you can create this object inside of this method
//and do not need to pass one in so remove it from the method signature
[HttpPost]
public ActionResult Create(string carId)
{
if (!ModelState.IsValid)
return View();
var carToRent = context.Cars.SingleOrDefault(c => c.Id == carId);
if (carToRent == null)
return Content($"Car not found!");
var rent = new Rent(); //this line has been added since the method signature was changed
rent.Car = carToRent;
var customer = context.Customers.SingleOrDefault(c => c.UserId == User.Identity.Name);
if (customer == null)
return Content($"Customer not found!");
rent.Customer = customer;
context.Rents.Add(rent);
context.SaveChanges();
return RedirectToAction("Index");
}
and finally you can remove the following:
//delete this action entirely, if youre doing nothing other than redirecting
//to an action then just link directly to the action you want
//notice the ActionLink in the view is modified to hit the Create action directly
public ActionResult Rent(string id)
{
return RedirectToAction("Create", "Rents");
}
As you can see below you can pass parameters in RedirectToAction() method.
RedirectToAction(String, String, RouteValueDictionary)
Redirects to the specified action using the action name, controller name, and route values. Try to redirect Create action with the carId and Rent object.
I dont know using multiple post object, but you can post one post object like that
public class MyPostObject
{
public string carId { get; set; }
public Rent rent{ get; set; }
}
and post it like that
[HttpPost]
public ActionResult Create(MyPostObject myPostObject)
{
string carId=myPostObject.carId;
Rent rent = myPostObject.rent;
....
}
UPDATE : Or you can use multiple post object with Ajax
$("#btnSave").on('click', function () {
var url = '#Url.Action("Create", "Rent")';
//Rent class properties
var data=
{
Brand: 'Renault',
Model: 'Megan',
};
$.ajax({
url:url,
type:"POST",
data:{
carId:'12',
rent:data
},
datatype:'json',
ContentType:'application/json;utf-8'
}).done(function(resp){
alert('Success ' +resp);
}).error(function(err){
alert("Error " + err.status);
});
});
As mentioned in the comments, you will have to pass the required parameters into the redirect statement.
public ActionResult Rent(string id)
{
Rent rentItem = new Rent();
return RedirectToAction("Create", "Rents", new { carId = id, rent = rentItem});
}
You either have not passed the parameters or you are missing the below method if you are looking to return a view with your redirect
public ActionResult Create()
{
return View();
}
I am creating a tracking application to keep track of some server and their build info. I want the user to fill out one single form that then performs a INSERT into two separate tables. Here is my code, but it only populates the "Server" table and not the "System_Build_Information" table. I have tried also changing the Bindings and even leaving them out all together. This code by the way is from my Server controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Server_Name,Server_Role,Server_OS")] Server server, [Bind(Include = "Model")] System_Build_Information sys)
{
if (ModelState.IsValid)
{
db.Servers.Add(server);
db.System_Build_Information.Add(sys);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Server_Role = new SelectList(db.Server_Roles, "Id", "Server_Role", server.Server_Role);
ViewBag.Server_OS = new SelectList(db.Operating_System, "Id", "Operating System", server.Server_OS);
string[] environments = new string[] { "Virtual", "Physical" };
ViewBag.Environments = new SelectList(environments);
return View(server);
}
I believe that the default model binder is not able to bind to the two classes that you have passed (and the Bind() statements may also be interfering). If you need to have information from two models placed into a view and POSTed back, create a new view model class that contains the models for both of those classes.
public class ServerInfoViewModel{
public Server ServerInfo {get;set;}
public System_Build_Information SystemBuildInfo {get;set;}
}
Now, send this view model back and forth to your view.
#model namespace.ServerInfoViewModel
and to use the properties in your form
#Html.TextBoxFor(x=>x.ServerInfo.Type)
#Html.TextBoxFor(x=>x.SystemBuildInfo.SomeField)
Lastly, you would need to update your POST action method to bind the new view model
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ServerInfoViewModel model)
{
if (ModelState.IsValid)
{
db.Servers.Add(model.ServerInfo);
db.System_Build_Information.Add(model.SystemBuildInfo);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Server_Role = new SelectList(db.Server_Roles, "Id", "Server_Role", server.Server_Role);
ViewBag.Server_OS = new SelectList(db.Operating_System, "Id", "Operating System", server.Server_OS);
string[] environments = new string[] { "Virtual", "Physical" };
ViewBag.Environments = new SelectList(environments);
return View(model);
}
I have created a database and then an MVC solution using VisualStudio Exrpess 2013 for WEB and
I created a ViewModel to display an Edit view with dropdown lists.
The view model includes just the property 'parlists' of type PartnerList, which is the model representing the main table of the database, and 2 properties of type SelectList which I use to create the dropdown lists in the view.
The code of the viewmodel is as follows:
public class FileStatusEdit
{
public SelectList HoldingsStatus { get; set; }
public SelectList RealGainStatus { get; set; }
public PartnerList parlists { get; set; }
}
In the controller I have the following code for the HttpGet edit method:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var viewModel = new FileStatusEdit
{
HoldingsStatus = new SelectList(db.Statuses, "Status_ID", "Status1", db.PartnerLists.Where(p => p.IntermediaryID == id).Single().AssetDataSource.HoldingsFile.Status_ID),
RealGainStatus = new SelectList(db.Statuses, "Status_ID", "Status1", db.PartnerLists.Where(p => p.IntermediaryID == id).Single().AssetDataSource.RealGainFile.Status_ID),
parlists = db.PartnerLists
.Include(p => p.AssetDataSource)
.Where(p => p.IntermediaryID == id)
.Single()
};
if (viewModel.parlists == null)
{
return HttpNotFound();
}
return View(viewModel);
}
This code is working fine and the view is correctly displaying a form with the dropdown lists. I omit the view code as it is quite long and propably not relevant.
So far so good.
My Http Post Edit method, however, is not saving the changes to the database. The code is as follows:
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(FileStatusEdit newParList)
{
if (TryUpdateModel(newParList.parlists, "",
new string[] { "Firstname", "Surname", "Category", "ClientID", "IntermediaryID", "ExternalRef", "RecordStatus", "Asset_Data_Source_ID", "New_Communication_Issued", "AssetDataSource", "HoldingsFile", "RealGainFile"}))
{
try
{
db.Entry(newParList.parlists).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException)
{
ModelState.AddModelError("", "Unable to save changes.");
}
}
return View(newParList);
}
As you can see I am passing the viewmodel (newParList) to the EditPost method and then I update it using TryUpdateModel. By stepping into the debugging process I can see that the database record newParList.parlists is correctly updated with the user input, however when the step db.SaveChanges() is executed the program redirects to the Index view without saving the changes to the database.
I tried using Attach as suggested in some posts but I believe the attach step is already included in the line 'db.Entry(newParList.parlists).State = EntityState.Modified;' and that did not indeed solve the problem.
I examined a lot of posts and tried different solutions but none of them worked so I would appreciate some help.
I suspect you're missing the context add or update.
Here's an example of how I handle the creation of a new record.
For an update you would find the record first then save the changes.
public void SaveCreatedMessage(Message message)
{
var dbEntry = _context.Message.Add(message);
if (dbEntry != null)
{
// Create the new record
dbEntry.CustomerID = message.CustomerID;
dbEntry.MessageID = message.MessageID;
dbEntry.Description = message.Description;
dbEntry.Text = message.Text;
dbEntry.IsRead = message.IsRead;
dbEntry.CreatedOn = message.CreatedOn;
dbEntry.CreatedBy = message.CreatedBy;
_context.Message.Add(message);
}
_context.SaveChanges();
}
I think I found the solution. I was not updating the correct entity.
In my HttpPost Edit method I now replaced the following line:
db.Entry(newParList.parlists).State = EntityState.Modified;
with:
db.Entry(newParList.parlists.AssetDataSource.HoldingsFile).State = EntityState.Modified;
db.Entry(newParList.parlists.AssetDataSource.RealGainFile).State = EntityState.Modified;
Now my entities HoldingsFile and RealGainFile are updated after SaveChages() is executed.
I'm working on a mvc4 project and I cant seem to figure out how to pass a newly created object from one page to another page.
on page A
im submitting a form to the database which is creating a new object in placing it into the table.
but after the form is submitted I want a confirmation page to appear afterwards. I dont know how to pull the newly created object from the previous page that saved it to the database. I could call it with the Id that was made but I dont know how to call it without knowing the id
public ActionResult Pay(int id,Paid paid)
{
PaidAdapter cAdapter = new PaidAdapter();
paid.CId = id;
if (ModelState.IsValid)
{
cAdapter.StorePaid(paid);
return RedirectToAction("SubmitPayment");
}
return View(paid);
}
public ActionResult SubmitPayment(int id)
{
var cAdapter = new PaidAdapter();
var model = cAdapter.GetPaidViewModel(id);
return View(model);
}
public Paid StorePaid (Paid paid)
FContext db = new FContext();
paid= db.Paid.Add(paid);
db.SaveChanges();
//return the paidId
return paid;
}
public PaidViewModel GetPaidViewModel(int id)
{
var model = new PaidViewModel();
FContext db = new FContext();
model.Paid= db.Paid.Where(c => c.PaidId == id).FirstOrDefault();
return model;
}
Use the overload of RedirectToAction that accepts route parameters. Assuming you're using the default route and it has an id parameter the code would be:
return RedirectToAction("SubmitPayment", new { id = paid.PaidId });
So I have my Viewbag that pulls through a dropdown list in my create razor view which uses the follownig Code:
Controller
ViewBag.ActionTypes = new SelectList(query.AsEnumerable(), "ActionTypeID", "ActionTypeTitle");
Then my Razor does this;
#Html.DropDownList("ActionTypeID", (IEnumerable<SelectListItem>)ViewBag.ActionTypes)
I'm stuck on what to put in my Post Method and how to get the value from the dropdown list?
can anyone point me in the right direction
Assuming you have a model called ActionType:
public class ActionType
{
public int ActionTypeID { get; set; }
...
}
Since your SelectList using this same property name, the ModelBinder will take care of setting the correct POSTed value:
new SelectList(query.AsEnumerable(), "ActionTypeID", "ActionTypeTitle");
--------------------------------------------^
Just remember to add a property with the same name to the Model class you're using in your Action:
-------------------------------v Add the property to this class.
[HttpPost]
public ActionResult Create(SomeModel model)
{
}
Here's an example. Imagine you have the following Entities already generated by Entity Framework:
Person Country
------- -------
PersonID CountryID
FullName Name
CountryID
[HttpPost]
public ActionResult Create(PersonModel model)
{
if (ModelState.IsValid)
{
DbEntities db = new DbEntities();
Person person = new Person();
person.FullName = model.FullName;
person.CountryID = model.CountryID; // This value is from the DropDownList.
db.AddToPersons(person);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
// Something went wrong. Redisplay form.
return View(model);
}
It's just a matter of setting the POSTed ID value to the foreign key property of the entity object.