I'm having some issues in updating and inserting records using ASP.NET MVC and Entity Framework.
I have a form (which is a report) that is dynamically created and can have any amount of questions. I'm trying to allow the user to edit the report and submit the changes so that it is updated in the database.
I am retrieving the report to be edited from the database via a repository then setting it to an instance of ModeratorReport. I'm then changing the value of the properties and using db.SaveChanges to save the changes to the database.
The problem is that it is not saving the changes.
Please could someone advise me on what I am doing wrong?
Here is the Edit Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection formCollection, int moderatorReportId, string status)
{
ModeratorReport reportToEdit = repository.GetModeratorReportById(moderatorReportId);
List<QuestionAnswer> originalReportAnswers = repository.GetAllModeratorReportAnswers(moderatorReportId, status).ToList();
foreach (QuestionAnswer answer in originalReportAnswers) {
reportToEdit.QuestionAnswers.Remove(answer);
}
int sectionID;
int questionID;
foreach (string key in formCollection.AllKeys)
{
var value = formCollection[key.ToString()];
Match m = Regex.Match(key, "section(\\d+)_question(\\d+)");
if (m.Success) {
QuestionAnswer newAnswer = new QuestionAnswer();
sectionID = Convert.ToInt16(m.Groups[1].Value.ToString());
questionID = Convert.ToInt16(m.Groups[2].Value.ToString());
newAnswer.ModeratorReportID = moderatorReportId;
newAnswer.SectionID = sectionID;
newAnswer.QuestionID = questionID;
newAnswer.Answer = value;
newAnswer.Status = "SAVED";
reportToEdit.QuestionAnswers.Add(newAnswer);
}
}
reportToEdit.Status = "SAVED";
AuditItem auditItem = new AuditItem();
auditItem.ModeratorReportID = moderatorReportId;
auditItem.Status = "SAVED";
auditItem.AuditDate = DateTime.Now;
auditItem.Description = "The Moderator report..."
auditItem.UserID = User.Identity.Name;
reportToEdit.Audit.Add(auditItem);
db.SaveChanges();
return RedirectToAction("Details", new { id = moderatorReportId });
}
The problem looks like you're just not setting reportToEdit's EntityState to modified. Like so:
reportToEdit.Audit.Add(auditItem);
reportToEdit.EntityState = EntityState.Modified;
db.SaveChanges();
For more information about the EntityState enumeration, see this MSDN article.
Related
I'm working on ASP.NET Boilerplate. I have the problem where I try to get a record from a table called Buildings and make an update on it. I get the record from database by:
var buildingApp = _buildingsAppService.getBuildingsById(buildingInput);
And after that, I make some changes on the data as follows:
buildingApp.streetName = Request["buildingaddress"];
buildingApp.isInHoush = Convert.ToBoolean(Request["buildingOutput.isInHoush"]);
buildingApp.houshName = Request["HoushName"];
And then copy the buildingApp to another object, which has the same properties, in order to pass the new object to update method as follows:
var updateBuildingInput = new UpdateBuidlingsInput()
{
Id = buildingApp.Id,
buildingID = buildingApp.buildingID,
numOfBuildingUnits = buildingApp.numOfBuildingUnits,
numOfFloors = buildingApp.numOfFloors,
streetName = buildingApp.streetName,
buildingNo = buildingApp.buildingNo,
neighborhoodID = buildingApp.neighborhoodID,
buildingTypeID = buildingApp.buildingTypeID,
GISMAP = buildingApp.GISMAP,
houshProperty = buildingApp.houshProperty,
houshName = buildingApp.houshName,
X = buildingApp.X,
Y = buildingApp.Y,
buildingName = buildingApp.buildingName,
isInHoush = buildingApp.isInHoush,
buildingUsesID = buildingApp.buildingUsesID
};
And the update method is as follows:
_buildingsAppService.update(updateBuildingInput);
The problem is when it executes the previous line, I get the following error:
System.InvalidOperationException: 'Attaching an entity of type 'TaawonMVC.Models.Buildings' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.'
I can see that when I initialize the object updateBuildingInput manually, the update method runs without error. But when it depends on the object obtained from database using buildingApp, the error happens. It seems like the get method gets data from database and keeps holding on to the record from database, and when I try to update the same record, the conflict happens. This is the whole action where all of get and update happens:
public ActionResult UpdateApplication (UpdateApplicationsInput model)
{
var updateApplication = new UpdateApplicationsInput();
updateApplication.buildingId = Convert.ToInt32(Request["buildingnumber"]);
updateApplication.buildingUnitId = Convert.ToInt32(Request["dropDownBuildingUnitApp"]);
//==== get building and unit related to application for update ======
var buildingInput = new GetBuidlingsInput()
{
Id = updateApplication.buildingId
};
var buildingUnitInput = new GetBuildingUnitsInput()
{
Id = updateApplication.buildingUnitId
};
var buildingApp = _buildingsAppService.getBuildingsById(buildingInput);
var buildingUnitApp = _buildingUnitsAppService.GetBuildingUnitsById(buildingUnitInput);
buildingApp.streetName = Request["buildingaddress"];
buildingApp.isInHoush = Convert.ToBoolean(Request["buildingOutput.isInHoush"]);
buildingApp.houshName = Request["HoushName"];
// buildingUnitApp.BuildingId = updateApplication.buildingId;
buildingUnitApp.ResidenceStatus = Request["residentstatus"];
// copy object getBuildingUnitInput to updateBuildingUnitInput
var updateBuildingUnitInput = new UpdateBuildingUnitsInput()
{
BuildingId = buildingUnitApp.BuildingId,
ResidentName = buildingUnitApp.ResidentName,
ResidenceStatus = buildingUnitApp.ResidenceStatus,
NumberOfFamilyMembers = buildingUnitApp.NumberOfFamilyMembers,
Floor = buildingUnitApp.Floor,
UnitContentsIds = buildingUnitApp.UnitContentsIds
};
//============================================
// copy object from getBuildingOutput to updateBuildingInput
var updateBuildingInput = new UpdateBuidlingsInput()
{
Id = buildingApp.Id,
buildingID = buildingApp.buildingID,
numOfBuildingUnits = buildingApp.numOfBuildingUnits,
numOfFloors = buildingApp.numOfFloors,
streetName = buildingApp.streetName,
buildingNo = buildingApp.buildingNo,
neighborhoodID = buildingApp.neighborhoodID,
buildingTypeID = buildingApp.buildingTypeID,
GISMAP = buildingApp.GISMAP,
houshProperty = buildingApp.houshProperty,
houshName = buildingApp.houshName,
X = buildingApp.X,
Y = buildingApp.Y,
buildingName = buildingApp.buildingName,
isInHoush = buildingApp.isInHoush,
buildingUsesID = buildingApp.buildingUsesID
};
//======================================================
updateApplication.Id = Convert.ToInt32(Request["applicationId"]);
updateApplication.fullName = model.fullName;
updateApplication.phoneNumber1 = model.phoneNumber1;
updateApplication.phoneNumber2 = model.phoneNumber2;
updateApplication.isThereFundingOrPreviousRestoration = model.isThereFundingOrPreviousRestoration;
updateApplication.isThereInterestedRepairingEntity = model.isThereInterestedRepairingEntity;
updateApplication.housingSince = model.housingSince;
updateApplication.previousRestorationSource = model.previousRestorationSource;
updateApplication.interestedRepairingEntityName = model.interestedRepairingEntityName;
updateApplication.PropertyOwnerShipId = Convert.ToInt32(Request["PropertyOwnerShip"]);
updateApplication.otherOwnershipType = model.otherOwnershipType;
updateApplication.interventionTypeId = Convert.ToInt32(Request["interventionTypeName"]);
updateApplication.otherRestorationType = model.otherRestorationType;
updateApplication.propertyStatusDescription = model.propertyStatusDescription;
updateApplication.requiredRestoration = model.requiredRestoration;
updateApplication.buildingId = Convert.ToInt32(Request["buildingnumber"]);
updateApplication.buildingUnitId = Convert.ToInt32(Request["dropDownBuildingUnitApp"]);
// ==== get of restoration types which it is multi select drop down list ======
var restorationTypes = Request["example-getting-started"];
string[] restorationTypesSplited = restorationTypes.Split(',');
byte[] restorationTypesArray = new byte[restorationTypesSplited.Length];
for (var i = 0; i < restorationTypesArray.Length; i++)
{
restorationTypesArray[i] = Convert.ToByte(restorationTypesSplited[i]);
}
updateApplication.restorationTypeIds = restorationTypesArray;
// ====== end of RestorationTypes
_buildingsAppService.update(updateBuildingInput);
_applicationsAppService.Update(updateApplication);
// _buildingUnitsAppService.Update(updateBuildingUnitInput);
// ==== get list of applications ==============
var applicationsUpdate = _applicationsAppService.getAllApplications();
var applicationsViewModel = new ApplicationsViewModel()
{
Applications = applicationsUpdate
};
return View("Applications", applicationsViewModel);
}
How ASP.NET Boilerplate template, which I use, makes CRUD Operation to database:
public class BuildingsManager : DomainService, IBuildingsManager
{
private readonly IRepository<Buildings> _repositoryBuildings;
public BuildingsManager(IRepository<Buildings> repositoryBuildings)
{
_repositoryBuildings = repositoryBuildings;
}
// create new building in table buildings .
public async Task<Buildings> create(Buildings entity)
{
var building = _repositoryBuildings.FirstOrDefault(x => x.Id == entity.Id);
if(building!=null)
{
throw new UserFriendlyException("Building is already exist");
}
else
{
return await _repositoryBuildings.InsertAsync(entity);
}
}
// delete a building from buildings table .
public void delete(int id)
{
try
{
var building = _repositoryBuildings.Get(id);
_repositoryBuildings.Delete(building);
}
catch (Exception)
{
throw new UserFriendlyException("Building is not exist");
}
}
public IEnumerable<Buildings> getAllList()
{
return _repositoryBuildings.GetAllIncluding(b => b.BuildingType, n => n.NeighboorHood,u=>u.BuildingUses);
}
public Buildings getBuildingsById(int id)
{
return _repositoryBuildings.Get(id);
}
public void update(Buildings entity)
{
_repositoryBuildings.Update(entity);
}
}
How can I solve this problem? Many thanks for help.
By creating a new entity (updateBuildingInput) with the same primary key as one you have already read in your context, Entity will throw an error when you attempt an operation on the new entity (as you have seen) as it is already tracking an entity with that primary key in the context.
If _buildingsAppService is a DbContext and all you need to do is make some changes to an entity, you can:
Read the entity
Make changes directly to that entity object
Call _buildingsAppService.SaveChanges()
SaveChanges() will:
Saves all changes made in this context to the underlying database.
When getting the record from db you can use .AsNoTracking()
Or if you really need to update an attached entity first locate the attached copy and detach it, then modify and update;
public async Task<bool> UpdateAsync<T>(T entity)
where T : class, IHasId
{
// check if entity is being tracked
var local = _context.Set<T>().Local.FirstOrDefault(x => x.Id.Equals(entity.Id));
// if entity is tracked detach it from context
if (local != null)
_context.Entry<T>(local).State = EntityState.Detached;
_context.Attach(entity).State = EntityState.Modified;
var result = await _context.SaveChangesAsync();
// detach entity if it was not tracked, otherwise it will be kept tracking
if(local == null)
_context.Entry(entity).State = EntityState.Detached;
return result > 0;
}
btw, IHasId is a simple interface to make Id property accessible for generic types;
public interface IHasId {
int Id { get; set; }
}
Use .AsNoTracking():
public class BuildingsManager : DomainService, IBuildingsManager
{
public Buildings getBuildingsById(int id)
{
return _repositoryBuildings.GetAll().AsNoTracking().First(b => b.Id == id);
}
// ...
}
I want to update some of my columns value using entity framework
My code is as follows
public static void UpdateData(string contentMenu, int eid) //Update data in database
{
using (var context = new CmsContext())
{
try
{
var objDataContent = new Content
{
MenuContent = contentMenu,
ModifiedDateTime = DateTime.Now
};
//Code to update only MenuContent and ModifiedDateTime whose id=eid
GetRecords();
}
catch (Exception ex)
{
//
}
}
}
There are lot of answers in S/O but none of them helped me.
I followed https://stackoverflow.com/a/5567616/4701699
How can I update these two columns on the basis of id parameters like MSSQL query
update [CMS Menu].[dbo].[Contents] set MenuContent = 'testing' ,
ModifiedDateTime ='2016-01-02 16:02:15.803' where ContentId=2
You Can Update Your Column Entries by Using Linq:-
Here Are Example Code:-
var data = db.Content.FirstOrDefault(x=>x.ContentId==eID);
data.ModiFiedDateTime = DateTime.Now;
data.MenuContent = contentMenu;
db.SaveChanges();
My controller:
[HttpPost]
public ActionResult Deposit(DepositTicket dt)
{
using (var db = new MatchGamingEntities())
{
MembershipUser currentUser = Membership.GetUser();
Guid UserId = (Guid)currentUser.ProviderUserKey;
var MyAccount = from a in db.Accounts
where a.UserId == UserId
select new Account{
AccountId = a.AccountId,
Balance = a.Balance
};
BankTransaction transaction = new BankTransaction();
transaction.Amount = dt.Amount;
transaction.AccountId = MyAccount.SingleOrDefault().AccountId;
transaction.Created = DateTime.Today;
transaction.TransactionType = "Credit";
Debug.Write("Amount: " + transaction.Amount + " AccountId " + transaction.AccountId);
db.BankTransactions.AddObject(transaction);
MyAccount.SingleOrDefault().Balance += transaction.Amount;
//Update Query
db.SaveChanges();
return View();
}
Where the comment "Update Query" is, is where I want to add an update query to updated the Account table with an Account object. I want to update the exisiting record, can this be done using the predefined functions for db.Accounts or would I have to write a linq query?
It seems like you are going to an awful lot of trouble to get a custom account object to then just want to update the original account record. I would suggest just working with the original account object and make the necessary updates, then when SaveChanges is called it will update your account record.
Here are my suggested changes
public ActionResult Deposit(DepositTicket dt)
{
using (var db = new MatchGamingEntities())
{
MembershipUser currentUser = Membership.GetUser();
Guid UserId = (Guid)currentUser.ProviderUserKey;
var MyAccount = db.Accounts.SingleOrDefault(a => a.UserId == UserId);
BankTransaction transaction = new BankTransaction();
transaction.Amount = dt.Amount;
transaction.AccountId = MyAccount.AccountId;
transaction.Created = DateTime.Today;
transaction.TransactionType = "Credit";
Debug.Write(String.Format("Amount: {0} AccountId: {1}", transaction.Amount, transaction.AccountId);
db.BankTransactions.AddObject(transaction);
MyAccount.Balance += transaction.Amount;
db.SaveChanges();
return View();
}
Hope this helps.
db.SaveChanges(); will do the update for you, unless you have predefined SP in db.
I want to update my model through a class and not a form in ASP.Net MVC 2.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(string wo_number)
{
WorkOrder wo = GetWorkOrder(wo_number);
UpdateModel(wo);
camprintRepository.Save();
return RedirectToAction("Details", new { wo_number = wo_number });
}
I'm pulling the information from an outside source and I want it to update the entities in my applications database.
public WorkOrder GetWorkOrder (string wo_number)
{
UniFile uFile = uSession.CreateUniFile("WO");
uFile.RecordID = wo_number;
WO wo = new WO();
wo.wo_id = wo_number;
wo.sales_product_code = uFile.ReadNamedField("Sales_Code").ToString();
wo.description = uFile.ReadNamedField("P_Desc").ToString();
wo.part_number = uFile.ReadNamedField("Desc").ToString();
wo.work_order_quantity = uFile.ReadNamedField("Qty_To_Mfg").ToString();
wo.sales_order_quantity = uFile.ReadNamedField("Sod_Qty").ToString();
GetWorkOrderOper(wo);
}
I am using LINQ to SQL and as you can see there are some child objects that branch off from each workorder.
public void GetWorkOrderOper(WorkOrder wo)
{
UniFile uFile = uSession.CreateUniFile("WPO");
string key = wo.wo_id + "*" + wo.first_routing_sequence;
while(key != wo.wo_id + "*")
{
uFile.RecordID = key;
WPO wpo = new WPO();
wpo.wpo_id = key;
wpo.next_sequence_number = uFile.ReadNamedField("Next_Seq").ToString();
wpo.run_hours = uFile.ReadNamedField("Plan_Run_Lbr_Time").ToString();
key = wo.wo_id + "*" + wpo.next_sequence_number;
wo.WPOs.Add(wpo);
}
}
This is not updating the models and I'm not sure why. Any help would be greatly appreciated
Seems like there is no SubmitChanges? Or is that in your UpdateModel?
Another cause might be no primary key in your underlying table
A bit of background to my issue - I've inherited a large C# MVC application, and I'm currently making a change to the main process in the app.
So originally, the user would upload an item, and that would be that. Now however, the user can select a checkbox in order to have the item delivered. If this checkbox is selected, the Delivery table is populated with the relevant details.
Here is the POST action in my Items controller for when the user originally uploads the item details:
[HttpPost]
public ActionResult AddItemDetails(Int64? itemId, Item item, FormCollection formValues)
{
if (formValues["cancelButton"] != null)
{
return RedirectToAction("Index");
}
if (formValues["backButton"] != null)
{
return RedirectToAction("AddItemStart");
}
string ImageGuid = formValues["ImageGUID"];
ViewData["Image_GUID"] = ImageGuid;
if (item.ImageID == null && String.IsNullOrEmpty(ImageGuid))
{
ModelState.AddModelError("Image", "Image is required.");
ViewData["Image"] = "Image is required.";
}
if (ModelState.IsValid)
{
item.SubmissionState = -1; // Unsubmitted;
/**********************ADDED 25/1/2011**************************/
item.ProductCode = formValues["ProductCode"];
item.Name = formValues["Name"];
string deliverySelection = formValues["deliverySelection"];
if (deliverySelection == "on")
{
item.DeliverySelection = "Yes";
Delivery c = new Delivery()
{
ProductCode = formValues["ProductCode"],
Name = formValues["Name"],
PhoneNo = formValues["PhoneNo"],
Address = formValues["Address"],
SubmissionDate = System.DateTime.Now
};
item.Delivery = c;
}
else
{
item.DeliverySelection = "No";
}
/*****************************END*******************************/
if (itemId.HasValue)
{
UpdateItemDetails(item, ImageGuid, this);
}
else
{
titleId = ItemServices.AddItem(item, ImageGuid);
}
return RedirectToAction("AddSubItemDetails", new { itemId = item.ItemID });
}
return View(item);
}
This works fine, and achieves the desired result. However, I am a little stuck on modifying the update action for in the Items controller. Here is what I have so far:
[HttpPost]
public ActionResult UpdateItemDetails(Int64 itemId, Item item, FormCollection formValues)
{
if (formValues["cancelButton"] != null)
{
return RedirectToAction("View", new { itemId = itemId });
}
string image = formValues["ImageGUID"];
ViewData["Image_GUID"] = ImageGuid;
if (item.ImageID == null && String.IsNullOrEmpty(ImageGuid))
{
ModelState.AddModelError("Image", "Image is required.");
}
if (ModelState.IsValid)
{
//**********************Added 31.01.2011****************************//
using (ModelContainer ctn = new ModelContainer())
{
string DeliverySelection = formValues["deliverySelection"];
if (deliverySelection == "on")
{
item.DeliverySelection = "Yes";
Delivery c = new Delivery()
{
ProductCode = formValues["ProductCode"],
Name = formValues["Name"],
PhoneNo = formValues["PhoneNo"],
Address = formValues["Address"],
SubmissionDate = System.DateTime.Now
};
ctn.Delierys.AddObject(c);
item.Delivery = c;
}
else
{
item.DeliverySelection = "No";
}
ctn.AddToItems(item);
ctn.SaveChanges();
UpdateItemDetails(item, ImageGuid, this);
return RedirectToAction("View", new { itemId = itemId });
}
return View("UpdateItemDetails", MasterPage, item);
}
Notice how this is slightly different and uses the UpdateItemDetails() to update the database. I was unsure what to do here as I do need to collect formvalues to insert into the Delivery database. Here is UpdateItemDetails:
private void UpdateItemDetails(Item item, string ImageFileGuid, ItemsController controller)
{
using (ModelContainer ctn = new ModelContainer())
{
Item existingData = ItemServices.GetCurrentUserItem(item.ItemID, ctn);
controller.UpdateModel(existingData);
existingData.UpdatedBy = UserServices.GetCurrentUSer().UserID;
existingData.UpdatedDate = DateTime.Now;
// If there is a value in this field, then the user has opted to upload
// a new cover.
//
if (!String.IsNullOrEmpty(ImageFileGuid))
{
// Create a new CoverImage object.
//
byte[] imageBytes = FileServices.GetBytesForFileGuid(Guid.Parse(ImageFileGuid));
Image newImage = new Image()
{
OriginalCLOB = imageBytes,
ThumbnailCLOB = ImageServices.CreateThumbnailFromOriginal(imageBytes),
HeaderCLOB = ImageServices.CreateHeaderFromOriginal(imageBytes),
FileName = "CoverImage"
};
existingData.Image = newImage;
}
ctn.SaveChanges();
}
}
The code working as above, throws the following error when I try to update the details:
System.Data.SqlClient.SqlException:
The conversion of a datetime2 data
type to a datetime data type resulted
in an out-of-range value. The
statement has been terminated.
This error is thrown at ctn.SaveChanges() in the update action.
So I suppose my first question is how to overcome this error, however without making changes that will affect the AddItemDetails action. My second question then would be, if that error was cleared up, would this be a correct way to go about updating?
I'd be very grateful for any pointers. If any more info is needed, just ask.
Thanks :)
I was looking into your error and found this answer to another question that gives a little more information about the DATETIME and DATETIME2 data types.
DATETIME supports 1753/1/1 to
"eternity" (9999/12/31), while
DATETIME2 support 0001/1/1 through
eternity.
If you check the data that is being submitted do you see anything anomalous? Do you have a date property in your item class that is being set to some default value that is invalid for the DATETIME field?
It looks like one of the dates in the object you are trying to save is DateTime.MinValue. It could be the submission date for example. Check your form data to see if the value is being updated ofmr the client correctly and go from there.
I have this issue currently because if someone mistakenly forgets a slash (e.g. 1/189 when they meant 1/1/89), TryUpdateModel() updates the model without error, translating it to the .NET DateTime of "1/1/0189".
But then the Save crashes with the "conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value".
So wow do I catch this before the Save?