Entity Framework won't save changes to model - c#

I'm using Database First in my website and i'm trying to update the data of one entry of the database but the changes are not being saved in one part but are working in another one.
This is where i populate the form with the model data or create a new one to reserve the ID:
public ActionResult Editar(int ID = 0)
{
var banner = new Banner();
var siteVM = new SiteViewModel();
if (ID == 0)
{
db.Banner.Add(banner);
db.SaveChanges();
}
else
{
banner = db.Banner.Find(ID);
siteVM.iePaginas = db.Pagina.ToList().Select(x => new SelectListItem
{
Value = x.ID.ToString(CultureInfo.InvariantCulture),
Text = x.nome.ToUpper()
}).ToList();
foreach (var pagina in siteVM.iePaginas)
{
foreach (var bannerPagina in banner.BannerPagina)
{
if (int.Parse(pagina.Value) == bannerPagina.paginaID)
{
pagina.Selected = true;
}
}
}
}
siteVM.Banner = banner;
return View(siteVM);
}
And this is where i save it:
[HttpPost]
public ViewResult Editar(SiteViewModel model)
{
try
{
var banner = model.Banner;
banner.rascunho = !banner.ativo;
if (banner.ID == 0)
{
db.Banner.Add(banner);
}
else
{
db.Entry(banner).State = EntityState.Modified;
}
//Remove the related pages
foreach (var source in db.BannerPagina.Where(x => x.bannerID == banner.ID))
{
db.BannerPagina.Remove(source);
}
//Record related pages
foreach (var pag in model.PaginasSelecionadas)
{
db.BannerPagina.Add(new BannerPagina { paginaID = int.Parse(pag.Value), bannerID = banner.ID });
}
db.SaveChanges();
ViewBag.Salvo = 1;
return View(model);
}
catch
{
ViewBag.Error = 1;
return View(model);
}
}
The db.Entry(banner).State = EntityState.Modified; doesn't work, i get the data in the model and my ModeState is valid but the data isn't changed on the database. Just below that i have:
db.BannerPagina.Remove(source); //BannerPagina contains a set of pages that are related to my banner so i can choose the pages i want this banner displayed
db.BannerPagina.Add(new BannerPagina { paginaID = int.Parse(pag.Value), bannerID = banner.ID });
And they work fine. I also have tried to use the attach method but it also doesn't work.
EDIT
Kind of found my problem, earlier i had changed the StoreGeneratedPattern option on my table properties to Computed instead of None (I did that so i could create an empty object in the database using it's default values), once i changed it back my changes started to be saved in the DB.
Is there a way to make the Computed configuration work ?

Related

Record not adding to SQL Server despite data being passed correctly? (ASP.NET WEB API and Angular)

I am trying to add two records to two separate entities using one action result.
My data is passing just fine, and no catches are met (implying that the database is saving the necessary changes and returning the appropriate Ok).
My two entities are related, one entity is linked to the other through a Foreign Key. The entities are created in such a way that the entity dependent on the other (for its foreign key) is created after the other.
The first entity (Branch) is created and is stored just fine as expected on the SQL Server. However, the second is not, and I am not sure why?
Both entities are set to auto-increment their primary keys.
My action result (within my web API is split into two separate methods):
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Branch.Add(branch);
db.SaveChanges();
var latestBranchRecordReader = db.Branch.OrderByDescending(x => x.BranchId).First();
var latestBranchRecord = latestBranchRecordReader.BranchId;
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
CreateBranchAddress(branchAddress);
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
public ActionResult CreateBranchAddress(BranchAddress newBranchAddress)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
db.BranchAddress.Add(newBranchAddress);
db.SaveChanges();
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Both db.SaveChanges() are hit when debugging, so I do not understand why nothing is being added to the database for the BranchAddress entity.
The data coming through newBranch of type BranchesVM is also correct (I get no POST 400 errors).
Are there any other possible reasons as to why it may not be adding?
Does it need to have 2 ActionResult? If it's possible to have just one, you could try this:
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Add(branch);
db.SaveChanges();
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
db.Add(branchAddress);
db.SaveChanges();
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
You might also want to wrap it using transaction to make sure both query executed successfully, otherwise it will rollback.
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
//START TRANSACTION
using (var transSQL = db.Database.BeginTransaction())
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Add(branch);
db.SaveChanges();
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
db.Add(branchAddress);
db.SaveChanges();
//SQL Commit
transSQL.commit();
return Ok();
}
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
I realized that I did not post enough information in my question to get an appropriate answer. However, I changed my catch to output an inner exception:
catch (Exception e)
{
return BadRequest(e.InnerException);
}
This allowed me to see what exactly was causing the error, that being one of the records exceeding its maximum size as set out in the table.

C# ASP.NET MVC read-only for data field not working

I am having some issues with an edit form that does not seem to work no matter what I use.
I have tried
[ReadOnly(true)] and [Editable(false)]
in the Model
#Html.EditorFor(model => model.SellingPrice, new { htmlAttributes = new { #class = "form-control", #readonly = true } })
in the View..
I have changed the controller with nothing about the field.
I have also tried changing the model to
{ get; private set; }
According to a other post on another site using the [ReadOnly(true)] in the model and nothing in the controller pointing to it, it should come up 'null' and it does not. It seems to give a value of 0 or what the field has in it. Depending on if I use it in the edit View or not.
This field in the table is a calculated field on the SQL side. Used only for viewing.
Below is the controller POST - 2 Versions that I have tried:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ProductMaster productMaster)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
if (ModelState.IsValid)
{
ProductMaster prodFromDB = db.ProductMaster.Single(x => x.ProductMasterId == productMaster.ProductMasterId);
prodFromDB.VendorId = productMaster.VendorId;
prodFromDB.Material = productMaster.Material;
prodFromDB.VendorRef = productMaster.VendorRef;
prodFromDB.CreatedDate = productMaster.CreatedDate;
prodFromDB.CreatedBy = productMaster.CreatedBy;
prodFromDB.LastUpdated = productMaster.LastUpdated;
prodFromDB.UpdatedBy = productMaster.UpdatedBy;
prodFromDB.UnitOfMeasure = productMaster.UnitOfMeasure;
prodFromDB.StandardCost = productMaster.StandardCost;
prodFromDB.AverageCost = productMaster.AverageCost;
prodFromDB.LastCost = productMaster.LastCost;
prodFromDB.IsPrimary = productMaster.IsPrimary;
var Manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
Manager.ChangeObjectState(prodFromDB, System.Data.Entity.EntityState.Modified);
db.SaveChanges();
sb.Append("Submitted");
return Content(sb.ToString());
}
else
{
foreach (var key in this.ViewData.ModelState.Keys)
{
foreach (var err in this.ViewData.ModelState[key].Errors)
{
sb.Append(err.ErrorMessage + "<br/>");
}
}
}
}
catch (Exception ex)
{
sb.Append("Error :" + ex.Message);
}
return Content(sb.ToString());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ProductMasterId,VendorId,Material,VendorRef,CreatedDate,CreatedBy,LastUpdated,UpdatedBy,UnitOfMeasure,StandardCost,AverageCost,LastCost,IsPrimary")] ProductMaster productMaster)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
if (ModelState.IsValid)
{
db.Entry(productMaster).State = EntityState.Modified;
db.SaveChanges();
sb.Append("Submitted");
return Content(sb.ToString());
}
else
{
foreach (var key in this.ViewData.ModelState.Keys)
{
foreach (var err in this.ViewData.ModelState[key].Errors)
{
sb.Append(err.ErrorMessage + "<br/>");
}
}
}
}
catch (Exception ex)
{
sb.Append("Error :" + ex.Message);
}
return Content(sb.ToString());
}
The error that I get is:
The column "SellingPrice" cannot be modified because it is either a computed column or is the result of a UNION operator
First thing would be to mark the Entity property with the DatabaseGeneratedOption of Computed:
public class Product
{
// ...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public decimal SellingPrice { get; internal set; }
// ...
}
The next thing would be to avoid code that sets an entity's EntityState to modified explicitly. So long as the DbContext is tracking the entity, this should be enough:
ProductMaster prodFromDB = db.ProductMaster.Single(x => x.ProductMasterId == productMaster.ProductMasterId);
prodFromDB.VendorId = productMaster.VendorId;
prodFromDB.Material = productMaster.Material;
prodFromDB.VendorRef = productMaster.VendorRef;
prodFromDB.CreatedDate = productMaster.CreatedDate;
prodFromDB.CreatedBy = productMaster.CreatedBy;
prodFromDB.LastUpdated = productMaster.LastUpdated;
prodFromDB.UpdatedBy = productMaster.UpdatedBy;
podFromDB.UnitOfMeasure = productMaster.UnitOfMeasure;
prodFromDB.StandardCost = productMaster.StandardCost;
prodFromDB.AverageCost = productMaster.AverageCost;
prodFromDB.LastCost = productMaster.LastCost;
prodFromDB.IsPrimary = productMaster.IsPrimary;
db.SaveChanges();
The reason is that lets say I have an entity with 20 columns and I update 5 of them from the view model, and only 2 of those values actually differ from the entity's current state. If I let the DbContext track the changes and just call SaveChanges then the resulting UPDATE SQL statement will be an update specifying just the columns which values actually changed. If instead I set the EntityState to Modified, the UPDATE SQL statement will include all columns, whether I set them or not, and whether they actually changed or not. Computed columns should be ignored, but why incur the cost/risk of updating/overwriting values you don't intend to have been changed?

How to do insert action on many-to-many relationship

I need your help please,
I do not know where to start, but I'm doing a recruitment management application with the asp.net mvc and wcf technologies, I have a management part of the offers, to start I have a many-to-many relationship between Offers and Candidates table.
Now I want that when the candidate chooses the offer that suits him, when he clicks on the apply button, the offer's id and the id of the candidate is registered in the CandidateOffre association table.
That's what I did, thank you for telling me what I miss so that the insert is done correctly,
Server side (wcf)
public CandidatDTO CreateCandidat(CandidatDTO c)
{
CandidatEmploi candidatEmp = new CandidatEmploi();
CSRMappers.MapCandidatDTOToEntity(c, candidatEmp);
if (c.OffreEmploi != null && c.OffreEmploi.Any())
{
foreach (var f in c.OffreEmploi)
{
if (f.IDOffre == 0)
{
OffreEmploi of = new OffreEmploi();
CSRMappers.MapOffreDTOToEntity(f, of);
candidatEmp.OffreEmploi.Add(of);
}
else
{
//look for offer
var of = CRE.OffreEmploi.Find(f.IDOffre);
if (of != null)
candidatEmp.OffreEmploi.Add(of);
}
}
}
CRE.CandidatEmploi.Add(candidatEmp);
CRE.SaveChanges();
c.IDCandidat = candidatEmp.IDCandidat;
return c;
}
Client side (MVC)
[HttpGet]
public ActionResult PostE(int id)
{
var candidat = new CSRServiceReference.CandidatDTO();
candidat.OffreEmploi = new List<OffreDTO>();
if (id > 0)
{
var offre = CREClient.GetOffre(id);
if(offre != null)
{
candidat.OffreEmploi.Add(offre);
}
}
candidat.Experiences = new List<CSRServiceReference.ExpProDTO>();
candidat.Formations = new List<CSRServiceReference.FormationDTO>();
candidat.ConnInfo = new List<CSRServiceReference.ConnaissInfoDTO>();
candidat.ConnLing = new List<CSRServiceReference.connaissLingDTO>();
return View(candidat);
}
[HttpPost, ActionName("PostE")]
public ActionResult PostEmp(CSRServiceReference.CandidatDTO candidat)
{
if (ModelState.IsValid)
{
candidat.StatutCandidat = "En attente";
var newC = CREClient.CreateCandidat(candidat);
return RedirectToAction("EditCandidat", "Home", new { id = newC.IDCandidat });
}
return View(candidat);
}
But still the association table is empty unfortunately,
I don't know where is the problem exactly: /
Thank you for answering me.

Same function for updating and Saving?

My code to use the function for updating is here and it works also
[HttpPost]
public bool SaveDefCompny(DefCompanyDTO DefCmpny)
{
using (RPDBEntities db = new RPDBEntities())
{
using (TransactionScope trans = new TransactionScope())
{
//the problem is here incase of saving
var UpdateDefCmpnyId = (from CmpnyId in db.DefCompanies
where CmpnyId.Id == DefCmpny.Id
select CmpnyId).First();
List<DefCompany> list = new List<DefCompany>();
list.Add(UpdateDefCmpnyId);
try
{
foreach (DefCompany DefCmpny1 in list)
{
DefCmpny1.Id = DefCmpny1.Id;
DefCmpny1.ShortName = DefCmpny.ShortName;
DefCmpny1.FullName = DefCmpny.FullName;
DefCmpny1.ContactPerson = DefCmpny.ContactPerson;
DefCmpny1.Address1 = DefCmpny.Address1;
DefCmpny1.CompanyCity = DefCmpny.CompanyCity;
DefCmpny1.CompanyState = DefCmpny.CompanyState;
DefCmpny1.CompanyCountry = DefCmpny.CompanyCountry;
DefCmpny1.ZipPostCode = DefCmpny.ZipPostCode;
DefCmpny1.TelArea = DefCmpny.TelArea;
DefCmpny1.CurrentCurrencyCode = DefCmpny.CurrentCurrencyCode;
db.SaveChanges();
trans.Complete();
}
}
catch (Exception ex)
{
}
}
return false;
}
}
when I try to save instead of updating the line of code
var UpdateDefCmpnyId = (from CmpnyId in db.DefCompanies
where CmpnyId.Id == DefCmpny.Id
select CmpnyId).First();
gives null value and hence saving fails because record is new and not present in database so how to handle null in case of saving how to use try catch so that when value is null it proceed to saving code that add
How about something along these lines:
var UpdateDefCmpnyId = (from CmpnyId in db.DefCompanies
where CmpnyId.Id == DefCmpny.Id
select CmpnyId).FirstOrDefault();
if(UpdateDefCmpnyId == null)
{
//insert
//(handle the id however you need to for insert. depending on your setup, you might be able to leave it empty and let the database put it in for you)
}
else
{
//update
//set the id as you do in the question
}

Does EF upsert have to be done manually?

I want to upsert reference members of an existing entity.
Do I have to write specific code for the upsert?
meaning: I have to check if I'm handling an existing reference member or a new one.
Is there any other simple way to do so?
What happens when you do only Save ?
public void SaveCofiguration(MamConfiguration_V1Ui itemUi)
{
var itemEf = mMamConfiguration_V1UiToEfConvertor.ConvertToNewEf(itemUi);
using (var maMDBEntities = new MaMDBEntities())
{
IDal<MamConfiguration_V1> mamConfigurationDal = mDalFactory.GetDal<MamConfiguration_V1>(maMDBEntities);
mamConfigurationDal.Save(itemEf);
}
}
public MamConfiguration_V1 GetById(object id)
{
id.ThrowIfNull("id");
int configurationId = Convert.ToInt32(id);
var result =
mMaMDBEntities.MamConfiguration_V1.SingleOrDefault(item => item.ConfigurationId == configurationId);
return result;
}
public MamConfiguration_V1 Save(MamConfiguration_V1 item)
{
item.ThrowIfNull("item");
var itemFromDB = GetById(item.ConfigurationId);
if (itemFromDB != null)
{
UpdateEfItem(itemFromDB, item);
// if (mMaMDBEntities.ObjectStateManager.GetObjectStateEntry(itemFromDB).State == EntityState.Detached)
// {
// mMaMDBEntities.MamConfiguration_V1.AddObject(itemFromDB);
// }
// Attached object tracks modifications automatically
mMaMDBEntities.SaveChanges();
return item;
}
private void UpdateEfItem(MamConfiguration_V1 itemFromDb, MamConfiguration_V1 itemFromUi)
{
itemFromDb.UpdatedDate = DateTime.Now;
itemFromDb.Description = itemFromUi.Description;
itemFromDb.StatusId = itemFromUi.StatusId;
itemFromDb.Name = itemFromUi.Name;
itemFromDb.NumericTraffic = itemFromUi.NumericTraffic;
itemFromDb.PercentageTraffic = itemFromUi.PercentageTraffic;
itemFromDb.Type = itemFromUi.NumericTraffic;
foreach (var item in itemFromDb.MamConfigurationToBrowser_V1.ToList())
{
if (itemFromUi.MamConfigurationToBrowser_V1.All(b => b.BrowserVersionId != item.BrowserVersionId))
{
mMaMDBEntities.MamConfigurationToBrowser_V1.DeleteObject(item);
}
}
for (int i = 0; i < itemFromUi.MamConfigurationToBrowser_V1.Count; i++)
{
var element = itemFromUi.MamConfigurationToBrowser_V1.ElementAt(i);
var item = itemFromDb.MamConfigurationToBrowser_V1.SingleOrDefault(b => b.BrowserVersionId == element.BrowserVersionId);
if (item != null)
{
// copy properties from element to item
}
else
{
element.Browser = mMaMDBEntities.Browsers.Single(browserItem =>
browserItem.BrowserID == element.BrowserID);
//element.MamConfiguration_V1 = itemFromDb;
//have also tried: element.MamConfiguration_V1 = null;
//element.MamConfiguration_V1Reference = null;
itemFromDb.MamConfigurationToBrowser_V1.Add(element);
}
}
}
But I would have expecte Save(itemUi) and SaveChanges() to work fine. No?
public void InsertOrUpdate(DbContext context, UEntity entity)
{
context.Entry(entity).State = entity.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
http://forums.asp.net/t/1889944.aspx/1
To avoid the overhead of a query and then insert, or throwing exceptions, you can take advantage of the underlying database support for merges or upserts.
This nuget package does the job pretty well: https://www.nuget.org/packages/FlexLabs.EntityFrameworkCore.Upsert/
Github: https://github.com/artiomchi/FlexLabs.Upsert
Example:
DataContext.DailyVisits
.Upsert(new DailyVisit
{
// new entity path
UserID = userID,
Date = DateTime.UtcNow.Date,
Visits = 1,
})
// duplicate checking fields
.On(v => new { v.UserID, v.Date })
.WhenMatched((old, #new) => new DailyVisit
{
// merge / upsert path
Visits = old.Visits + 1,
})
.RunAsync();
The underlying generated sql does a proper upsert. This command runs right away and does not use change tracking, so that is one limitation.
See 'AddOrUpdate' method of System.Data.Entity.Migrations.
http://msdn.microsoft.com/en-us/library/system.data.entity.migrations.idbsetextensions.addorupdate%28v=vs.103%29.aspx
using System.Data.Entity.Migrations;
public void Save(Person person) {
var db = new MyDbContext();
db.People.AddOrUpdate(person);
db.SaveChanges();
}
"optimistic" approach for simple scenarios (demos)...
dbContext.Find()'s intellisense help tells us that it either retrieves entity by key if already present in current context, or queries the database to get it... then we know if it exists to either add or update. i'm using EFCore v2.2.0.
var existing = _context.Find<InventoryItem>(new object[] {item.ProductId});
if (existing == null) _context.Add(item);
else existing.Quantity = item.Quantity;
_context.SaveChanges();
DbContext.Update Method
For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state. This helps ensure new entities will be inserted, while existing entities will be updated. An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.
For entity types without generated keys, the state set is always Modified.
read this article
you can use this sample

Categories

Resources