I am trying to pass a value which is stored in one controller to another, code is below:
Charities Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Donate([Bind(Include = "ID,DisplayName,Date,Amount,Comment")] Charity charity)
{
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(charity.Comment))
{
var comment = charity.Comment.ToLower().Replace("hot", "###").Replace("cold", "###").Replace("Slow", "###").Replace("enjoy", "###").Replace("BAD", "###");
charity.Comment = comment; //Replaces textx from model variable - comment
charity.TaxBonus = 0.20 * charity.Amount;
}
if (string.IsNullOrEmpty(charity.DisplayName))
{
charity.DisplayName = "Annonymus"; //If user doesnt enter name then Annonymus
}
db.Donations.Add(charity);
db.SaveChanges();
TempData["Name"] = charity.DisplayName;
TempData["Amount"] = charity.Amount;
TempData["Comment"] = charity.Comment;
return RedirectToAction("../Payments/Payment", "Charities", new { id = charity.Amount });
}
return View(charity);
}
Charities Class
public class Charity
{
public int ID { get; set; }
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage = "Use letters only please")]
public string DisplayName { get; set; }
[DataType(DataType.Currency)]
[Range(2, Int32.MaxValue, ErrorMessage = "Atleast £2.00 or a whole number please")]
public int Amount { get; set; }
[DataType(DataType.Currency)]
public Double TaxBonus { get; set; }
public String Comment { get; set; }
public static object Information { get; internal set; }
}
Payment Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Payment([Bind(Include = "ID,CardName,CardNumber,ValidFrom,Expires,CardSecurityCode,EmailAddress,ConfrimEmailAddress,Address,City,Country,PostCode")] Payment payment)
{
if (ModelState.IsValid)
{
db.Payments.Add(payment);
db.SaveChanges();
TempData["Name"] = charity.DisplayName
TempData["Amount"];
TempData["Comment"];
TempData["Name"] = payment.CardName;
TempData["Email"] = payment.EmailAddress;
return RedirectToAction("Confirmation", "Payments", new { id = payment.ID });
}
return View(payment);
}
Payment Class
public class Payment
{
public int ID { get; set; }
}
public class CharityDBContext : DbContext //controls information in database
{
public DbSet<Charity> Donations { get; set; } //creates a donation database
}
public class PaymentDBContext : DbContext //controls information in database
{
public DbSet<Payment> Payments { get; set; } //creates a donation database
public System.Data.Entity.DbSet<CharitySite.Models.Charity> Charities { get; set; }
}
}
I am trying to get this from the Charities Controller
TempData["Name"] = charity.DisplayName;
To display in Payment controller
TempData["Name"] = charity.DisplayName;
Right now theres a squigly red line under "charity" in the payment controller with the message - doesnt exist in current context. I just wanted to know if it is possible to pass data from different controllers using temp data.
First of all, the line return RedirectToAction in your Donate method is going to send a 302 response to your browser which will issue a GET request to the url in the location header of the response, which in this case is Payment/Payment. But your Payment method is marked with HttpPost. Are you sure you want to send a second GET request to a method marked with HttpPost to save some part of the data(Payment) you want to save ?
I think you should save your charity and payment info in the same action method( Create a PaymentCharity view model and use that instead of using the Bind attribute and the entity classes created by EF to transfer data from your view to action method). Also, insteaof using TempData to pass data, What you should do is, get the unique id of the Payment record you saved, pass that in querystring to the second action method and in that using the unique payment id,read the payment record again and use that.
So in your Donate method,
public ActionResult Donate(PaymentCharirtVm model)
{
var charity = new Charity { DisplayName =model.Amount,Comment =model.Comment};
var payment = new Payment ();
//set the properties of payment here
db.Donations.Add(charity);
db.SaveChanges();
//now save Payment
db.Payment.Add(payment);
db.SaveChanges();
return RedirectToAction("Confirmation","Payment", new { id=payment.Id });
}
I have never tried to do it with tempdata. Have never needed to. I recomend you add the string with the charity name to the view model used by the payment controller.
Please correct code as below to retrieve value from TempData.
charity.DisplayName= TempData["Name"]
As already answered here, most answer are correct. It would be better to use routeValues in the RedirectToAction method to pass value from one action to another action in the same controller or different controller.
return RedirectToAction("actionName", "anotherControllerName", new { id = charity.DisplayName });
In the another controller, data can be retrieved by
string displayName = RouteData.Values["id"].ToString();
Here, in the routeValues, you can pass the whole Charity object as well and do some casting in the another controller to get the object properly in the that case when you need more than one properties to be sent:
return RedirectToAction("actionName", "anotherControllerName", new { id = charity });
And get it by:
Charity chatiry = (Charity)RouteData.Values["id"];
I hope that you will get some idea from this.
Related
This is my view model.
public class ProductViewModel
{
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsAvailable { get; set; }
}
When form is posted from client the form is submitted to this Controller
public async Task<IHttpActionResult> AddProduct(ProductViewModel productViewModel)
{
await ServiceInstances.PostAsync("product/add", productViewModel);
return Ok();
}
Then this controller submit the form to the API controller
Which is on my separate Project.
[HttpPost]
[Route("add")]
public IHttpActionResult AddProduct(ProductViewModel model)
{
_productService.AddProduct(model.UserServiceDetails());
return Ok();
}
Extension UserServiceDetails Where i get the Login User Info
public static UserServiceDetailModel<T> UserServiceDetails<T>(this T model)
{
var serviceRequestModel = new ServiceRequestModel<T>()
{
Model = model,
LoginInfo = HttpContext.Current.User.Identity.GetUserLoginInfo();
};
}
AddProductService:
public void AddProduct(UserServiceDetailModel<ProductViewModel> serviceRequestModel)
{
var repo = _genericUnitOfWork.GetRepository<Product, Guid>();
var mapped = _mapper.Map<ProductViewModel, Product>(serviceRequestModel.Model);
mapped.Id = Guid.NewGuid();
mapped.CreatedDate = GeneralService.CurrentDate();
mapped.CreatedById = serviceRequestModel.LoginInfo.UserId;
repo.Add(mapped);
_genericUnitOfWork.SaveChanges();
}
Now my question is Is there any way to assign the value to this field CreatedDate and CreatedById before posting it to service?
Reduce these logic to mapper:
mapped.CreatedDate = GeneralService.CurrentDate();
mapped.CreatedById = serviceRequestModel.LoginInfo.UserId;
Or is there any way that those field gets mapped to Product when
var mapped = _mapper.Map<ProductViewModel, Product>(serviceRequestModel.Model);
Sometime i may have the List<T> on view-model and there i have to add this field using the loop.
So this same mapping may get repeated over and over on Add Method Or Update.
In some entity i have to assign the ModifiedDate and ModifiedById also.
My Mapper Configuration:
public class ProductMapper : Profile
{
public ProductMapper()
{
CreateMap<ProductViewModel, Product>();
}
}
I cannot add the Enitity as IAuditableEntity and Overrride in ApplicationDbContext because my DbContext is in separate Project and i donot have access to Identity there.
I want to add a location to a user (My userDto has a list of location he wants to visit)
[HttpPut]
[Route("{id:guid}/location/")]
public IHttpActionResult AddLocationToUser(Guid idUser , LocationDto location)
{
_userLogic.AddLocationToUser(idUser, location);
return Ok();
}
it's ok to make a Put ? (because I have a function who just add a location to a list in user and then I want to update ) but how my route should look like ???
It's ok [Route("{id:guid}/location/")] ??
I pass the userId from session , but It's ok to send the whole location in PUT??
Since it is adding/creating a record, you want to use HttpPost. For LocationDto, you want to use [FromBody].
For example,
[HttpPost]
[Route("{id:guid}/location")]
public IHttpActionResult AddLocationToUser(Guid id, [FromBody] LocationDto location)
{
_userLogic.AddLocationToUser(id, location);
return Ok();
}
I second #Win's suggestion of using HttpPost. If you are using Web Api 2, then the following is another alternative.
Controller:
public class LocationController : ApiController
{
UserLogic _userLogic;
public LocationController()
{
_userLogic = new UserLogic();
}
public void PostLocationToUser(LocationViewModel locationViewModel)
{
_userLogic.AddLocationToUser(locationViewModel.UserId, locationViewModel.Location);
}
}
View Model:
public class LocationViewModel
{
public Guid UserId { get; set; }
public Location Location { get; set; }
}
public class Location
{
public string Latitude { get; set; }
public string Longitude { get; set; }
}
JSON:
var input = {
UserId: "11111111-1111-1111-1111-111111111111",
Location: {
Latitude: "anotherLatitude",
Longitude: "anotherLongitude"
}
};
I used the default Web Api routing, the url is "/api/Location", and the PostLocationToUser returns a status code of 204 (no content).
For simplicity and testing, i'm doing this from a new project and then apply it to my actual project once i understand the problem.
I made a model, named Person, which contains a List property, named ServiceNeeded. Users in the front end may encode as much string of services as they wish, so the input field for ServiceNeeded is dynamically created. In the POST method, those string input binds as expected. I save the Person object into the database, and works as expected. When I try to retrieve the people objects from the database, all but ServicesNeeded are present.
Here are my codes
Model (Person.cs):
public class Person
{
public Guid Id { get; set; }
public String Name { get; set; }
public List<String> ServiceNeeded { get; set; }
public Person()
{
this.ServiceNeeded = new List<String>();
}
}
Controller(Index and [POST]Create methods):
public ActionResult Index()
{
var x = db.People.ToList();
return View(db.People.ToList());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,ServiceNeeded")] Person person)
{
if (ModelState.IsValid)
{
person.Id = Guid.NewGuid();
db.People.Add(person);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
As I said the input fields for ServicesNeeded are dynamically create but properly bind to the model.
Here are some screenshots during runtime:
After user do some input:
The I added a variable before Index returns the View to check in runtime:
As seen, the same Person is present and everything but ServicesNeeded. I am fairly new with ASP.NET MVC and web development in general. I do handle other collections in my project, although are collection of objects. Only this particular case I do not understand. Where could my error be?
You need to tell the dbcontext to load the references if lazy loading is not enabled.
First change ServiceNeeded to a virtual property
public virtual List<String> ServiceNeeded { get; set; }
then load the property when you pull down the person by using Include
var x = db.People.Include("ServiceNeeded").ToList();
return View(x);
Article on loading related entities https://msdn.microsoft.com/en-us/data/jj574232.aspx
Look in the database; are the records there? I think the issue is that the ServicesNeeded collection is not persisted to the database; try adding:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,ServiceNeeded")] Person person)
{
if (ModelState.IsValid)
{
person.Id = Guid.NewGuid();
db.People.Add(person);
foreach (var service in person.ServicesNeeded)
db.ServicesNeeded.Add(service);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
I'm don't believe the relationships attached to the Person object auto-persist on SaveChanges(), and are probably not making it to the database.
I think the best idea that suits Entity Frame work is to create another Model for your 'ServiceNeeded', add it to your 'DbContext', save any services in the corresponding DbSet and retrieve them using .Include in LINQ. After this lengthy introduction look at the following codes:
In your Models:
public class MyService{
public Guid Id {get;set;}
public string ServiceName {get;set;}
public Guid PersonId {get;set;}
}
public class Person
{
public Guid Id { get; set; }
public String Name { get; set; }
public virtual IList<MyService> ServiceNeeded { get; set; }
}
In your ApplicationDbContext:
public System.Data.Entity.DbSet<MyService> MyServices { get; set; }
public System.Data.Entity.DbSet<Person> People { get; set; }
In your POST ActionResult:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,ServiceNeeded")] Person person, List<String> ServiceNeeded)
{
if (ModelState.IsValid)
{
db.People.Add(person);
db.SaveChanges();
db.Entry(person).GetDatabaseValues();
foreach (string service in ServiceNeeded){
db.MyServices.Add( new MyService {ServiceName = service,
PersonId = person.Id})
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
Please note that I removed person.Id = Guid.NewGuid(); so that the Id
be generated by Database Identity and then I re-get the person.Id so that I create the MyService record.
and finally as #JamieD77 suggested, in your GET ActionResult:
public ActionResult Index()
{
var x = db.People.Include("ServiceNeeded").ToList();
return View(x);
}
I'm having trouble passing a viewmodel into a view. I have two views: a Search view and a GeneralForm view. Search passes search results into GeneralForm's viewmodel.
Say the GeneralForm is a complex viewmodel that holds two other viewmodels:
public class GeneralFormViewModel
{
public GeneralInfoViewModel GeneralInfo { get; set; }
public NotesViewModel Notes { get; set; }
}
public class GeneralInfoViewModel
{
[Required(ErrorMessage = "Please enter the person's name.")]
[DisplayName("Name:")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter the person's ID.")]
[DisplayName("ID:")]
public int ID { get; set; }
}
public class NotesViewModel
{ // etc.
(I set up this way in order to use multiple #Html.BeginForms on my GeneralForm view. In this way, I hope to POST and validate small sections of the entire general form, one at a time, using KnockoutJS and AJAX.)
[HttpPost]
public ActionResult Search(SearchViewModel vm)
{
var query = // do some searching
var viewmodel = new GeneralFormViewModel()
{
GeneralInfo = new GeneralInformationViewModel
{
ID = query.id,
Name = query.name
}
};
return RedirectToAction("GeneralForm", viewmodel);
}
At this point, viewmodel.GeneralInfo is not null, and the viewmodel is passed to the GeneralForm controller.
[HttpGet]
public ActionResult GeneralForm(GeneralFormViewModel model)
{
return View(model);
}
Now model.GeneralInfo is null. What conventions of MVC am I breaking by doing this, and how can I get the GeneralForm view to render the data acquired via the Search controller to the GeneralForm view?
Problem is You can't send data with a RedirectAction.
you're doing a 301 redirection and that goes back to the client.
Store it in a TempData or Session ...
See the following post:
passing model and parameter with RedirectToAction
I want to have a drop down that won't require to to query the database in the controller POST section in order to get the ID for the drop down selection so that it can be placed in table as a foreign key. I don't understand how it could be down without needing to make that query. I want entity framework to do the heavy lifting for it if that makes sense? Is this possible?
public class BillRate
{
public BillRate()
{
this.BillRateTickets = new List<Ticket>();
}
public long BillRateID { get; set; }
public decimal TicketBillRate { get; set; }
public virtual ICollection<Ticket> BillRateTickets { get; set; }
}
public class Ticket
{
public long TicketID { get; set; }
public virtual BillRate BillRate { get; set; }
}
It's not clear what exactly do you mean. If you do not query your database where where do you think the items to be displayed on the dropdown list will come from? They will definetely won't come from the view because in HTML when you submit a form containing a <select> element, only the selected value is ever sent to the server. The collection values are never sent, so ASP.NET MVC cannot invent those values for you.
If you want to avoid hitting your database you could store this list into the cache and inside your POST action try looking for the values in the cache first. But those values must be persisted somewhere on your server. So you could have a method which will look for the values in the cache first and if not found query the database:
private IEnumerable<Ticket> GetTickets()
{
// Try to get the tickets from the cache first:
var tickets = MemoryCache.Default["tickets"] as IEnumerable<Ticket>;
if (tickets == null)
{
// not found in cache, let's fetch them from the database:
tickets = db.Tickets.ToList();
// and now store them into the cache so that next time they will be available
MemoryCache.Default.Add("tickets", tickets, new CacheItemPolicy { Priority = CacheItemPriority.NotRemovable });
}
return tickets;
}
and then you could have the 2 controller actions:
public ActionResult Index()
{
var model = new BillRate();
model.BillRateTickets = GetTickets();
return View(model);
}
[HttpPost]
public ActionResult Index(BillRate model)
{
model.BillRateTickets = GetTickets();
return View(model);
}