I created a post method in my Web API to do a partial update of one table - to change Order Status field in the table. here is what I got so far:
public IHttpActionResult UpdateOrderStatus(Order ord)
{
try
{
if (!ModelState.IsValid)
{
throw new ProcessException("One or more values are invalid, please check.");
}
using (MyContext ctx = new MyContext())
{
ord.StatusID = 3;
ctx.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = ord.OrderID }, ord);
}
}
catch (Exception ex)
{
throw new ProcessException(ex.Message);
}
}
I am stepping through the code and everything seems to be working - no errors, but the database is not getting updated. What am I doing wrong?
Update:
Set the StatusID in the object BEFORE passing it to the Web API method:
var ord = { "OrderID": 1, "OrderDate": CurrentDate, "StatusID": 3};
public IHttpActionResult UpdateOrderStatus(Order ord)
{
try
{
if (!ModelState.IsValid)
{
throw new ProcessException("One or more values are invalid, please check.");
}
using (MyContext ctx = new MyContext())
{
ctx.Entry(ord).State = EntityState.Modified;
ctx.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = ord.OrderID }, ord);
}
}
catch (Exception ex)
{
throw new ProcessException(ex.Message);
}
}
If you're using EntityFramework then you need to inform the context about the Order object you are updating. In EF6 you would typically have a DbSet property on the context called Orders, so I'd advise adding ctx.Orders.Attach(Order) before you set the StatusID
Try this code:
Get orderEntity from database by comparing with ord.OrderID, Then update StatusID of that record.
using (MyContext ctx = new MyContext())
{
var orderEntity = ctx.Order.FirstOrDefault(x=> x.OrderID == ord.OrderID);
orderEntity.StatusID = 3;
ctx.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = ord.OrderID }, ord);
}
Related
I am using EF Core 5.0.
I have a class called JobApplicationManager that OwnsMany() of JobApplications.
modelBuilder.Entity<JobApplicationManager>().OwnsMany(s => s.JobApplicationCategories, a =>
{
a.Property<DateTime>("CreatedDate");
a.Property<DateTime>("UpdatedDate");
a.ToTable("JobApplicationManagerCategories");
});
modelBuilder.Entity<JobApplication>().OwnsMany(s => s.JobApplicationCategories, a =>
{
a.Property<DateTime>("CreatedDate");
a.Property<DateTime>("UpdatedDate");
a.ToTable("JobApplicationCategories");
});
Current state: There is one JobApplicationManager in the DB with one JobApplication.
Action: I add another JobApplication to the JobApplicationManager.
In my DBContext.SaveChangesAsync() I call ChangeTracker.Entries() and the following is the entities:
As you can see the first entry is the JobApplication with status of Added.
Also in this DBContect you can see the JobApplicationManager has two JobApplications:
The next step I can ChangeTracker.HasChanges() which returns false so therefor base.SaveChangesAsync() returns 0 number of entities written.
I dont know what to do.
Here is the code that creates my entity and calls my repository:
var mgr = await AMRepository.GetJobApplicationManagerAsync(command.JobApplicationManagerID);
var newJobApplication = mgr.AddJobApplication(command.JobDescriptionURL, command.JobTitle, command.FileName, command.CompanyName, command.GeographicLocation, command.RecruiterName, command.JobApplicationCategories);
// Update the Repository
AMRepository.Update(mgr);
commandResult = await AMRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
Here is the code in my Repository:
public void Update(JobApplicationManager applicationManager)
{
try
{
AppManagerContext.Entry(applicationManager).State = EntityState.Modified;
}
Here is the code in my DBContext for SaveEntitiesAsync():
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var changesFlag1 = ChangeTracker.HasChanges();
// Dont save the enumeration Value Object types
try
{
var enumerationEntries = ChangeTracker.Entries()
.Where(x => EnumerationTypes.Contains(x.Entity.GetType()));
foreach (var enumerationEntry in enumerationEntries)
{
enumerationEntry.State = EntityState.Unchanged;
}
}
catch (Exception exc)
{
Console.WriteLine(exc.ToString());
}
UpdateShadowProperties();
var numberOfStateEntitiesWritten = 0;
try
{
numberOfStateEntitiesWritten = await base.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateException exc)
{
Console.WriteLine(exc.ToString());
}
if (numberOfStateEntitiesWritten == 0)
{
return false;
}
return true;
}
Here is the code from the Repository for GetJobApplicationManagersAsync():
public async Task<IEnumerable<JobApplicationManager>> GetJobApplicationManagersAsync() => await AppManagerContext.JobApplicationManagers.ToListAsync();
Within mgr.AddApplication() all I do is add POCO classes to the mgr. There is no infrastructure related activities (i.e. using DBContext)>
Using a Web API, I want to update one record and two m-to-n relation tables in the PUT method. For this I found the update method of Entity Framework. However, my code further down does nothing at all. Is it possible that I have fundamentally misunderstood something there?
[HttpPut("Roles/{roleID}")]
public async Task<ActionResult> UpdateRole(int roleID, Role updatedRole) {
// Validate data
if (roleID != updatedRole.ID)
return BadRequest("ID missmatch.");
if (!updatedRole.Validate(out string problems))
return BadRequest(problems);
// Apply modified relations
updatedRole.ApplyModifiedRelations();
// Setup db connection
MpaContext db = _mpaContext;
Response.RegisterForDispose(db);
// Save changes
db.Update(updatedRole);
// I've added these two afterwords, but it also doesn't update anything
if (updatedRole.HasModifiedClientIDs)
db.UpdateRange(updatedRole.ClientRoles);
if(updatedRole.HasModifiedViewerIDs)
db.UpdateRange(updatedRole.RoleViewers);
try {
await db.SaveChangesAsync();
} catch (Exception ex) {
return Problem(ex.Message);
}
return Ok();
}
public void ApplyModifiedRelations() {
if (HasModifiedClientIDs) {
ClientRoles = new List<ClientRole>(
_ClientIDs.Select(c => new ClientRole() { ClientID = c, RoleID = ID })
);
}
if (HasModifiedViewerIDs) {
RoleViewers = new List<RoleViewer>(
_ViewerIDs.Select(v => new RoleViewer() { RoleID = ID, ViewerID = v })
);
}
}
I think your object didn't exist in db context (because you get it from request). So EF can't update it.
Try this
db.Entry(updatedRole).State = EntityState.Modified;
db.SaveChanges();
I found the solution. You have to actually load the relationship before updating it. I modified my ApplyModifiedRelations method accordingly:
public async Task ApplyModifiedRelations(MpaContext db) {
if (HasModifiedClientIDs) {
await db.Entry(this).Collection(r => r.ClientRoles).LoadAsync();
ClientRoles = new List<ClientRole>(
_ClientIDs.Select(c => new ClientRole() { ClientID = c, RoleID = ID })
);
}
if (HasModifiedViewerIDs) {
await db.Entry(this).Collection(r => r.RoleViewers).LoadAsync();
RoleViewers = new List<RoleViewer>(
_ViewerIDs.Select(v => new RoleViewer() { RoleID = ID, ViewerID = v })
);
}
}
I also moved that method call below the db.Update call. But I don't know, if this makes any difference.
I want to check if in the GET request (this is GET request) there is only customer id provided and not other parameters. How should I check this in the current context and which is the exception that I should return?
public JObject GetStatus(GetStatusRequest request)
{
var responseArr = new JArray();
var customers = GetCustomers(request.CustomerId);
foreach (var c in customers)
{
responseArr.Add(FormatRequest(customers));
}
return new JObject
{
["customers"] = responseArr
};
}
Using asp.net core 2.1 I'd do it like so...
public class CustomerListResponse {
public List<YourCustomerClassName> Customers {get;set;}
}
public ActionResult<CustomerListResponse> GetStatus(GetStatusRequest request)
{
// check request
if(doyourcheckhere == false) {
return BadRequest();
}
// load your data here. Do not think in JArray and JObject
// simply use POCOs
var customers = GetCustomers(request.CustomerId);
// if you need to reformat, create separate class and use e.g. automapper
return new CustomerListResponse {
Customers = customers
};
}
You can try this:
[HttpGet]
public HttpResponseMessage GetStatus(GetStatusRequest request)
{
try
{
if (request.CustomerId>0 && String.IsNullOrEmpty(request.Customername) /*&& other conditions*/)
{
var customers = GetCustomers(request.CustomerId);
return Request.CreateResponse(HttpStatusCode.OK, customers );
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Your custom error message here");
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, new HttpError(ex.Message));
}
}
I have a project I am using a database that is connected thru the entity data model and I am using the DBcontext to create and update entities.
My Create method (below) is working fine.
[HttpPost]
public IHttpActionResult PostCustomer([FromBody] Customer Customer)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (var dbCtx = new CustomerDBEntities())
{
dbCtx.Customers.Add(Customer);
dbCtx.SaveChanges();
}
return CreatedAtRoute("DefaultApi", new { id = Customer.id }, Customer);
}
But my update method is doing nothing. I am not getting an error or anything just nothing seems to happen. and it is not updating the values.
The code is this
[HttpPut]
public IHttpActionResult UpdateCustomer([FromBody] Customer Customer)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Customer cust;
var ctx = new CustomerDBEntities();
cust = ctx.Customers.Where(s => s.id == Customer.id).FirstOrDefault<Customer>();
if (cust != null)
{
cust.Customer_Name = Customer.Customer_Name;
cust.email = Customer.email;
cust.Customer_address = Customer.Customer_address;
}
ctx.Entry(cust).State = System.Data.EntityState.Modified;
ctx.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = Customer.id }, Customer);
}
When I was using an SQL command before to go directly to database it was working fine but ever since I changed it it stopped working.
Any help would be much appreciated.
Remove this line of code.
ctx.Entry(cust).State = System.Data.EntityState.Modified;
EF tracks the changes made to entities in the context, you do not need to tell it that it was modified.
Add changes at Property Level
context.Entry(Customer).Property(cust => cust.Customer_Name).IsModified = true;
More Details here https://msdn.microsoft.com/en-us/data/jj592677
Also similar question answered here https://stackoverflow.com/a/15339512/1481690
I have an MVC Action Controller method as:
public ActionResult Register(ViewModel.User model)
{
DbContextTransaction dbTransaction = null;
if (ModelState.IsValid)
{
try
{
UserAccountLogic registerUser = new UserAccountLogic();
dbTransaction = registerUser.RegisterUser(model);
Mail.SendEmail(model.Email, model.FirstName);
dbTransaction.Commit();
//Session["Email"] = model.Email;
ViewBag.Style = "block";
}
catch (Exception e)
{
dbTransaction.Rollback();
log.Error(e.Message, e);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
RegisteredUser function is in Business Layer as:
public DbContextTransaction RegisterUser(User model)
{
//var userEntity = Mapper.Map<User, Dal.UserInfo>(model);
var userEntity = MapModelToEntity(model);
var registerService = new Dal.AccountService();
return registerService.SaveRegisterDetails(userEntity);
}
and SaveRegisterDetails function is in DataAccessLayer as:
public DbContextTransaction SaveRegisterDetails(UserInfo registerDetails)
{
//TransactionModel transactionModel = new TransactionModel();
using (HealthCarePortalEntities context = new HealthCarePortalEntities())
{
using (DbContextTransaction dbTran = context.Database.BeginTransaction())
{
context.UserInfo.Add(registerDetails);
context.SaveChanges();
return dbTran;
}
}
Now my problem is I want to rollback a transaction i.e a newly registered user data from the database when there is any exception in sending activation link to the user. But the problem is the part where I am doing rollback an exception throws because the Database connection is null.
So my question is how can I get the connection of DAL layer in Controller which is in another layer.
Thanks for the help.
You only need to implement IDisposable
private void Dispose(bool disposing)
{
// ...
if (disposing && (this._innerConnection != null))
{
this._disposing = true;
this.Rollback(); // there you go
}
}
Implement IDisposable
You could use the ambient transaction model.
using(TransactionScope tran = new TransactionScope()) {
UserAccountLogic registerUser = new UserAccountLogic();
dbTransaction = registerUser.RegisterUser(model);
Mail.SendEmail(model.Email, model.FirstName);
tran.Complete();
}
That way if an exception occurs inside your transaction scope the db rolls back the data.