Why transaction does not work? Works only first .savechanges(); - asp.net-mvc

I need to do 2 savechanges. But first of them does not work. Ready method has simple view that says Thank you, #Viewbag.OrderName
Сontroller
public class Some : Controller
{
private AppDbContext context;
public Some(AppDbContext ctx)
{
context = ctx;
}
[Httpost]
public ActionResult Buy(int? id, string Price,Order order)
{
if (id == null) return RedirectToAction("Index");
using (var transaction = context.Database.BeginTransaction())
{
context.Orders.Add(order)
context.SaveChanges();
if(Price!=null)
{var ty=context.Phones.Where(p =>
p.PhoneId==id).FirstOrDefault();
ty.Price1=Price+150;
}
context.SaveChanges();
transaction.Commit();
}
return View("Buy");
}
public ActionResult Ready(int? id, string OrderName)
{
ViewBag.PhoneId = id;
ViewBag.OrderName = OrderName;
return View();
}
}
}
Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration["Data:AppDb:ConnectionString"]));
services.AddTransient<Some>();}
Context class
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AviaAppDbContext> options)
: base(options)
{
}
public DbSet<Phone> Phones { get; set; }
public DbSet<Order> Orders { get; set; }
appsettings
"AllowedHosts": "*",
"Data": {
"AppDb": {
"ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=AppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Added view for more information
View 1
foreach p in Model
<form method="post">
<td class="pricelight"><input type="radio" name="price"value="#p.PriceLight.ToString("")"/>#p.PriceLight.ToString("")</td>
<td class="fromtabletime"><input type="radio" name="price"value="#p.PriceOk.ToString("")/>#p.PriceOk.ToString("")</td>
<td class="totable"><input type="radio" name="price"value="#p.PriceHigh.ToString("")/>#p.PriceHigh.ToString("")</td>
<td class="button13"><button type="submit" asp-action="Buy" asp-route-id="#p.PhoneId" asp-controller="Home">Next</button></td>
</form>
View 2
#using System.Linq;
#using System;
#model List<Searchphone>
#{
ViewBag.Title = "PhoneId";
}
<h2 class="orderinformation">Order Data</h2>
<form method="post" class="formpass" asp-action="Ready" asp-controller="Some">
<input type="hidden" id="PhoneId"value="#ViewBag.PhoneId" name="PhoneId">
<label for="OrderSurName">Surname</label><br>
<input type="text" placeholder="Enter Surname" name="OrderSurName" required><br>
<label for="OrderName">Имя</label><br>
<input type="text" placeholder="Enter Name" name="OrderName" required><br>
<button type="submit" class="button7">Next</button>
</form>
I have checked it without Transaction. It works the same. I can not make changes in two tables,one context. What is my mistake?

Your AppDbContext doesn't have a parameterless constructor, and you are trying to invoke one in the using statement. Also, you already are injecting the DbContext with DI so its already initialized and ready to use. With that said you don't need to create a new instance of the context, just use the existing one that you already passed in trough DI.
Hope it helps

If you want to edit Phones data which PhoneId equals to the id parameter, you need change
context.Price1=Price+150; to ty.Price1=Price+150;
More code as follow:
[Httpost]
public ActionResult Buy(int? id, string Price,Order order)
{
if (id == null) return RedirectToAction("Index");
using (var transaction = context.Database.BeginTransaction())
{
context.Orders.Add(order)
context.SaveChanges();
if(Price!=null)
{var ty=context.Phones.Where(p =>
p.PhoneId==id).FirstOrDefault();
ty.Price1=Price+150;
}
context.SaveChanges();
transaction.Commit();
}
return View("Buy");
}

Related

How to Submit Value, but Receive Key from Input field ASP.NET Core MVC

I have an id received in my action parameter. From where I got my scheme details. By this details I found my desired scheme name. And now I want to show this name in my .cshtml as an input field where I want to show the value in "read-only" format (not changeable). So in my controller I sent it inside a ViewData instead of SelectList as shown below:
ViewData["SchemeNum"] = schemeInfo.SchemeNum;
instead of
ViewData["SchemeInfoId"] = new SelectList(_context.PackagewiseSchemeInfo.Where(p => p.Id == id), "Id", "SchemeNum", schemeInfo.SchemeNum);
I know if I use a selectlist, it would have been easier for me to catch the Key after form submitting like mentioned above -- "Id", "SchemeNum" ...
HTML:
<input class="form-control" value="#ViewBag.SchemeNum" readonly />
Now, I'm getting SchemeNum instead of Id after form submission. I want to know how to catch the KEY instead of value by an input tag? Please help.
You probably wanna disable your input add a hidden field for the id.
<input class="form-control" value="#ViewBag.SchemeNum" disabled />
<input type="hidden" value="#ViewBag.SchemeId" />
Besides,you could custom Model Binder to get the matched key of the value.
Here is a working demo like below:
Model:
public class PackagewiseSchemeInfo
{
public int Id { get; set; }
public string SchemeNum { get; set; }
}
View:
#model PackagewiseSchemeInfo
<form asp-action="Test">
<input asp-for="SchemeNum" class="form-control" value="#ViewBag.SchemeNum" readonly />
<input type="submit" value="create" />
</form>
Custom Model Binder:
public class CustomModelBinder : IModelBinder
{
private readonly YourDbContext _context;
public CustomModelBinder(YourDbContext context)
{
_context = context;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var data = bindingContext.ActionContext.HttpContext.Request.Form["SchemeNum"];
var model = _context.PackagewiseSchemeInfo.Where(a => a.SchemeNum == data.ToString())
.Select(a=>a.Id).FirstOrDefault();
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Controller:
public IActionResult Index()
{
var schemeInfo = new PackagewiseSchemeInfo()
{
Id = 1,
SchemeNum = "aaa"
};
ViewData["SchemeNum"] = schemeInfo.SchemeNum;
return View();
}
[HttpPost]
public IActionResult Test([ModelBinder(typeof(CustomModelBinder))]int Id)
{
//do your stuff...
}
Result:

ASP.Net Core 3 Sort Foreign Key Drop down Select Lists

I have Two different ASP.net Core 3 applications that are running which have Tables linked to other tables with foreign keys. In the CRUD pages the Drop down lists work properly and display the data but the data is sorted in the order of the primary key, which is typically an integer index key. I am hoping to figure out how to display these dropdown lists in sort order based on the values being displayed. I have not found any commands that do the job. Any help would be appreciated. The foreign Keys for Projects are Street and TownProperty. I will attach the code files;
Projects Controller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using TOGProjects.Models;
namespace TOGProjects.Controllers
{
public class ProjectsController : Controller
{
private readonly TOGProjectsContext _context;
public ProjectsController(TOGProjectsContext context)
{
_context = context;
}
// GET: Projects
public async Task<IActionResult> Index()
{
var tOGProjectsContext = _context.Projects.Include(p => p.Street).Include(p => p.TownProperty);
return View(await tOGProjectsContext.ToListAsync());
}
// add Search ablity on the Index Page
[HttpGet]
public async Task<IActionResult> Index(String Projectsearch)
{
ViewData["GetProjectDetails"] = Projectsearch;
var projectquery = _context.Projects
.Include(s => s.Street)
.Include(s => s.TownProperty)
.AsQueryable();
if (!String.IsNullOrEmpty(Projectsearch))
{
projectquery = projectquery.Where(x => x.ProjectDescription.Contains(Projectsearch) || x.Pwnum.Contains(Projectsearch));
}
return View(await projectquery.AsNoTracking().ToListAsync());
}
// GET: Projects/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var projects = await _context.Projects
.Include(p => p.Street)
.Include(p => p.TownProperty)
.FirstOrDefaultAsync(m => m.ProjectId == id);
if (projects == null)
{
return NotFound();
}
return View(projects);
}
// GET: Projects/Create
public IActionResult Create()
{
ViewData["StreetId"] = new SelectList(_context.StreetNames, "StreetId", "Streets");
ViewData["TownPropertyId"] = new SelectList(_context.TownProperties, "TownPropertyId", "TownPropertyName");
return View();
}
// POST: Projects/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ProjectId,ProjectDescription,DateStarted,Pwnum,StreetId,StateProjectNumber,TownPropertyId,CapitalAccount")] Projects projects)
{
if (ModelState.IsValid)
{
_context.Add(projects);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["StreetId"] = new SelectList(_context.StreetNames, "StreetId", "Streets", projects.StreetId);
ViewData["TownPropertyId"] = new SelectList(_context.TownProperties, "TownPropertyId", "TownPropertyName", projects.TownPropertyId);
return View(projects);
}
// GET: Projects/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var projects = await _context.Projects.FindAsync(id);
if (projects == null)
{
return NotFound();
}
ViewData["StreetId"] = new SelectList(_context.StreetNames, "StreetId", "Streets", projects.StreetId);
ViewData["TownPropertyId"] = new SelectList(_context.TownProperties, "TownPropertyId", "TownPropertyName", projects.TownPropertyId);
return View(projects);
}
// POST: Projects/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ProjectId,ProjectDescription,DateStarted,Pwnum,StreetId,StateProjectNumber,TownPropertyId,CapitalAccount")] Projects projects)
{
if (id != projects.ProjectId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(projects);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProjectsExists(projects.ProjectId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["StreetId"] = new SelectList(_context.StreetNames, "StreetId", "Streets", projects.StreetId);
ViewData["TownPropertyId"] = new SelectList(_context.TownProperties, "TownPropertyId", "TownPropertyName", projects.TownPropertyId);
return View(projects);
}
// GET: Projects/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var projects = await _context.Projects
.Include(p => p.Street)
.Include(p => p.TownProperty)
.FirstOrDefaultAsync(m => m.ProjectId == id);
if (projects == null)
{
return NotFound();
}
return View(projects);
}
// POST: Projects/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var projects = await _context.Projects.FindAsync(id);
_context.Projects.Remove(projects);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool ProjectsExists(int id)
{
return _context.Projects.Any(e => e.ProjectId == id);
}
}
}
Projects.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace TOGProjects.Models
{
public partial class Projects
{
public int ProjectId { get; set; }
[DisplayName("Project Description")]
[Required, StringLength(50)]
[RegularExpression(#"(([A-za-z0-9\s\-]+))$")]
public string ProjectDescription { get; set; }
[DisplayName("Date Started")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime? DateStarted { get; set; }
[DisplayName("Engineering Project Number")]
public string Pwnum { get; set; }
public int? StreetId { get; set; }
[DisplayName("State Project Number")]
[StringLength(15)]
public string StateProjectNumber { get; set; }
public int? TownPropertyId { get; set; }
[DisplayName("Capital Account")]
[StringLength(30)]
public string CapitalAccount { get; set; }
public virtual StreetNames Street { get; set; }
[DisplayName("Town Property Name")]
[StringLength(50)]
public virtual TownProperties TownProperty { get; set; }
}
}
Projects Index.cshtml
#model TOGProjects.Models.Projects
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit</h1>
<h4>Projects</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ProjectId" />
<div class="form-group">
<label asp-for="ProjectDescription" class="control-label"></label>
<input asp-for="ProjectDescription" class="form-control" />
<span asp-validation-for="ProjectDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateStarted" class="control-label"></label>
<input asp-for="DateStarted" class="form-control" />
<span asp-validation-for="DateStarted" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Pwnum" class="control-label"></label>
<input asp-for="Pwnum" class="form-control" />
<span asp-validation-for="Pwnum" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StreetId" class="control-label"></label>
<select asp-for="StreetId" class="form-control" asp-items="ViewBag.StreetId"></select>
<span asp-validation-for="StreetId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StateProjectNumber" class="control-label"></label>
<input asp-for="StateProjectNumber" class="form-control" />
<span asp-validation-for="StateProjectNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="TownPropertyId" class="control-label"></label>
<select asp-for="TownPropertyId" class="form-control" asp-items="ViewBag.TownPropertyId"></select>
<span asp-validation-for="TownPropertyId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CapitalAccount" class="control-label"></label>
<input asp-for="CapitalAccount" class="form-control" />
<span asp-validation-for="CapitalAccount" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Thanks
You will need to add an OrderBy to your data like below.
ViewData["StreetId"] = new SelectList(_context.StreetNames.OrderBy(s => s.Streets), "StreetId", "Streets");
ViewData["TownPropertyId"] = new SelectList(_context.TownProperties.OrderBy(t => t.TownPropertyName), "TownPropertyId", "TownPropertyName");

MVC Return Fields to Controller

I've got a controller to retrieve and return values for my drop down, and a second, that when an option from the dropdown is selected, uses the values (Title and ID) in an API Request.
Controllers
public ActionResult GetEpics()
{
//Code to retrieve list
Epics = new GetEpicsViewModel();
Epics.Epics = epicsList;
return View(Epics);
}
[HttpPost]
public ActionResult Build(GetEpicsViewModel epic)
{
GetEpicsViewModel epicTest = epic;
//API Request
return View();
}
This is displayed in my drop down list as below:
View
#using (Html.BeginForm("Build", "GetEpics", FormMethod.Post))
{
<label for="input_OutputType"> Process: #Html.DropDownListFor(model => model.Id, new SelectList(Model.Epics, "Id", "Title")) </label>
<input type="submit" value="Submit" />
}
This works fine, but how would I then go about passing both the Title and ID to my controller?
I can pass the ID through fine, but cant figure out how to pass the Title as well.
Screenshot
Models
public class DevOpsEpic
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
and
public class GetEpicsViewModel
{
public string Id { get; set; }
public string Title { get; set; }
public List<DevOpsEpic> Epics { get; set; }
}
Realise this is probably a really simple answer, but just cant figure it out!
You can use jQuery for that, so when your dropdown is changed, set title value in hidden file.
#using (Html.BeginForm("Build", "GetEpics", FormMethod.Post))
{
<label for="input_OutputType"> Process: #Html.DropDownListFor(model => model.Id, new SelectList(Model.Epics, "Id", "Title"),new { name = "Id" }) </label>
<input type="hidden" id="Title" name="Title" />
<input type="submit" value="Submit" />
}
$('#dropdownId').change(function(){
$('#Title').val($('#dropdownId option:selected').text());
});

show data from viewmodel in view

I tried this code but I have error like this:
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[XNet.Repository.Model.RoomType]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`1[XNet.Repository.Model.EditRoomTypeViewModel]'.
I don't know, whats part give an error. Please help.
my service
public List<EditRoomTypeViewModel> GetViewRoom(int RoomTypeID)
{
List<RoomType> roomTypes = (from d in _RoomTypeRepository.All()
select d).ToList();
List<EditRoomTypeViewModel> editRoomTypeViewModel = new List<EditRoomTypeViewModel>();
foreach (RoomType roomType in roomTypes)
{
editRoomTypeViewModel.Add(new EditRoomTypeViewModel
{
RoomTypeID = RoomTypeID,
RoomTypeName = roomType.RoomtypeName,
RoomTypeDescription = roomType.RoomTypeDescripton,
});
}
return editRoomTypeViewModel;
}
my controller
public ActionResult Room()
{
ViewBag.hotel = _hotelService.GetByID(2).HotelName;
List<EditRoomTypeViewModel> editRoomTypeViewModel = _roomViewService.GetViewRoom(_HotelID);
return View(editRoomTypeViewModel.FirstOrDefault());
}
my view model
public class EditRoomTypeViewModel
{
public int RoomTypeID { get; set; }
public string RoomTypeName { get; set; }
public string RoomTypeDescription { get; set; }
}
my view
#model IEnumerable<XNet.Repository.Model.EditRoomTypeViewModel>
#{
ViewBag.Title = "Room";
}
<h2>Room</h2>
<div>
#Html.Label("Hotel Name");
</div>
<div>
#ViewBag.hotel
</div>
<table>
#foreach (var a in Model)
{
<tr>
<td>
#Html.DisplayFor(model => a.RoomTypeName)
</td>
<td>
<input style="width:100px;" type="button" title="EditRoomType" value="Edit" onclick="location.href='#Url.Action("EditRoom", "Hotel", new { RoomTypeID = a.RoomTypeID})'" />
</td>
</tr>
}
</table>
<input style="width:200px;" type="button" title="EditRoomType" value="New Room Type" onclick="location.href='#Url.Action("NewRoom", "Hotel") '" />
I noticed that you returned just one editRoomTypeViewModel object in your controller, but in your view you declared the model as IEnumerable<XNet.Repository.Model.EditRoomTypeViewModel>.
Another point is that the error seems to be related to an assignment of ViewBag somewhere else, cause it contains thisdictionaryrequires a model item of type and probablt the only thing that is of type dictionary is ViewBag.
Just remove the .FirstOrDefault() in the controller action and you should be good to go.
public ActionResult Room()
{
ViewBag.hotel = _hotelService.GetByID(2).HotelName;
List<EditRoomTypeViewModel> editRoomTypeViewModel = _roomViewService.GetViewRoom(_HotelID);
return View(editRoomTypeViewModel);
}

UpdateModel not updating my model

I must have something incorrectly setup as I can't get the UpdateModel function to correctly update my model based on information passed in via a FormCollection.
My View looks like:
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
<p>ID: #Html.TextBox("ID", Model.ID)</p>
<p>Forename: #Html.TextBox("Forename", Model.Forename)</p>
<p>Surname: #Html.TextBox("Surname", Model.Surname)</p>
<input type="submit" value="Submit" />
}
</fieldset>
My model is:
namespace NSLM.Models
{
public class Person
{
public int ID;
public string Forename;
public string Surname;
}
}
and my controller is:
[HttpPost]
public ActionResult Details(FormCollection collection)
{
try
{
// TODO: Add update logic here
Models.Person m = new Models.Person();
// This doesn't work i.e. the model is not updated with the form values
TryUpdateModel(m);
// This does work
int.TryParse(Request.Form["ID"], out m.ID);
m.Forename = Request.Form["Forename"];
m.Surname = Request.Form["Surname"];
return View(m);
}
catch
{
return View();
}
}
as you can see if I manually assign each property it works fine, so what have I not set that would get the model to be updated with the form values?
Thanks,
Mark
Replace fields with properties in your model, i.e.:
namespace NSLM.Models
{
public class Person
{
public int ID {get; set;}
public string Forename {get; set;}
public string Surname {get; set;}
}
}
By the time the call gets to the action method any automatic model binding has already been performed. Try changing the input parameter of your action method to accept a Person instance. In that case the model binder will try to create the instance and populate it from the values passed by your form.
try this :
view :
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
<p>Forename: #Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</p>
<p>Surname: #Html.EditorFor(model => model.Surname)
#Html.ValidationMessageFor(model => model.Surname)
</p>
<input type="submit" value="Submit" />
}
</fieldset>
Controller :
[HttpPost]
public ActionResult Details(Person p)
{
if (ModelState.IsValid)
{
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(p);
}

Resources