Reputation: 121
I have a controller method in which I'm trying to save a form that was edited in the view.
[HttpPost]
public ActionResult ModifyContract(ModContract mod)
{
// submit modified contract
if (ModelState.IsValid)
{
OutlookMediaEntities1 db = new OutlookMediaEntities1();
db.ObjectStateManager.ChangeObjectState(mod, EntityState.Modified);
// save changes
db.SaveChanges();
return RedirectToAction("ContractDetails", "Contract", new { id = (int) ViewData["contractid"] });
}
else
{
ModelState.AddModelError("", "Missing necessary information");
return View();
}
}
I'm getting a InvalidOperationException on the db.ObjectStateManager... line. I saw something that said it might be because I didn't assign a primary key and noticed my ModAds class (below) didn't include a primary key. Added it but I get the same error. I tried doing a db.Attach(mod) as suggested by an answerer in my earlier question, but I get a compiler error. Attach takes as a parameter a "IEntityWithKey". So apparently my entity doesn't have a key, so I'm thinking something must be wrong with how I'm declaring the model stuff.
public class ModContract
{
public int contract_id; // primary key?
public string contract_name { get; set; }
public List<ModAds> ads;
public string print_product_id { get; set; }
public string print_ad_option_id { get; set; }
}
public class ModAds
{
public int contr_ad_id; // primary key?
public string name;
public string product_name;
public string adv_product;
public List<string> editions;
public double freq_disc;
public double other_dis_dol;
public double? other_dis_per;
public string non_cash_note;
public double non_cash_cons;
}
Is there a way to specify what the primary key is? My model is kind of weird because I have a list of ModAds in ModContract as you can see. Not sure if that could be a problem. Also I tried adding getters and setters to all of the field to see if that was the problem but I got the same errors.
I also tried to save with db.Entry(model).State = EntityState.Modified;
but it didn't work because I'm using EF 4.0.
Any help is greatly appreciated!
EDIT
I think I know what's wrong, the model was made from a bunch of different database tables, i.e. there's no table ModContract. I'm pretty sure I have to somehow disperse the model stuff into the appropriate tables and columns..
EDIT 2
Pastebin of OutlookMediaEntities1: http://pastebin.com/YyrhjGDA
EDIT 3
Here's the GET ModifyContract method that shows how I'm querying the database to make the model.
public ActionResult ModifyContract(int id)
{
ViewData["contractid"] = id; // save id
// get data from db
OutlookMediaEntities1 db = new OutlookMediaEntities1();
var cont = from printContractAd in db.print_contract_ads
from printContracts in db.print_contracts
where printContracts.contract_id == id
select new { printContracts.contract_id, printContracts.note }; // name
// select the ads for this contract
var ads = from printContractAd in db.print_contract_ads
from printAdOption in db.print_ad_option
from printContract in db.print_contracts
from printProduct in db.print_products
from contractAdCount in db.Contract_ad_edition_count
where printContractAd.print_ad_option_id == printAdOption.print_ad_option_id &&
printContract.contract_id == printContractAd.contract_id &&
printProduct.print_product_id == printAdOption.print_product_id &&
contractAdCount.contract_ad_id == printContractAd.contract_ad_id &&
printContract.contract_id == id
select new
{
printContractAd,
printAdOption,
printProduct,
printContract.IsSigned,
contractAdCount.CountFreq
};
ModContract con = new ModContract();
foreach (var c in cont) // only one
{
con.contract_id = c.contract_id;
con.contract_name = c.note;
break; // only one
}
List<ModAds> cads = new List<ModAds>();
foreach (var ad in ads)
{
ModAds mad = new ModAds();
mad.contr_ad_id = ad.printContractAd.contract_ad_id;
mad.product_name = ad.printProduct.product_name;
mad.adv_product = ad.printAdOption.name;
mad.name = ad.printContractAd.note;
mad.freq_disc = ad.printContractAd.frequency_discount;
mad.other_dis_dol = ad.printContractAd.other_discount;
mad.other_dis_per = ad.printContractAd.other_discount_percent;
mad.non_cash_note = ad.printContractAd.non_cash_note;
mad.non_cash_cons = ad.printContractAd.non_cash_discount; // consideration?
// get editions
var eds = from contractEditions in db.print_contract_editions
where contractEditions.contract_ad_id == ad.printContractAd.contract_ad_id
select new
{
contractEditions
};
mad.editions = new List<string>();
foreach (var ed in eds)
{
mad.editions.Add("Volume " + ed.contractEditions.vol.ToString() + " Issue " + ed.contractEditions.issue.ToString());
}
cads.Add(mad);
}
con.ads = cads;
// bind dropdownlist ad options
var printproductList = from printProduct in db.print_products
select printProduct;
List<SelectListItem> products_list = new List<SelectListItem>();
List<print_products> products = printproductList.ToList();
foreach (print_products product in products)
{
SelectListItem temp_item = new SelectListItem();
temp_item.Text = product.product_name;
temp_item.Value = product.print_product_id.ToString();
products_list.Add(temp_item);
}
ViewData["products_list"] = new SelectList((IEnumerable<SelectListItem>)products_list.ToList(), "Value", "Text");
// return view with form to modify
return View(con);
}
EDIT 4
Here's the view too if it helps (I couldn't get it format right on here): http://pastebin.com/SZvzJ9FB
Upvotes: 1
Views: 122
Reputation: 5967
for updating an entity , the entity must be attached to Data Context
.for doing this getting the entity from data context with Single
method would be enough.
try changing your code like this :
[HttpPost]
public ActionResult ModifyContract(ModContract mod)
{
// submit modified contract
if (ModelState.IsValid)
{
OutlookMediaEntities1 db = new OutlookMediaEntities1();
var updatedMod = db.ModContract.Single(m=>m.contract_id == mod.contract_id);
TryUpdateModel<ModContract>(updatedMod);
// save changes
db.SaveChanges();
return RedirectToAction("ContractDetails", "Contract", new { id = (int) ViewData["contractid"] });
}
else
{
ModelState.AddModelError("", "Missing necessary information");
return View();
}
}
Upvotes: 1
Reputation: 17108
You could tell EF that contract_id
is the key:
public class ModContract
{
[Key]
public int contract_id;
}
and make sure that the database column that the property maps to is a PK
. It may matter though that it's an IDENTITY
or not based on how you use it.
Upvotes: 0
Reputation: 19222
You can add the [Key] attribute above your primary key in the ModAds class.
Upvotes: 0