I have the post method which creates a new cream
public ActionResult CreateCream(CreamModel cream, string creamTypeId)
{
if (ModelState.IsValid)
{
if (creamTypeId != string.Empty)
{
try
{
cream.CreamTypeModel_id = int.Parse(creamTypeId);
creamManager.CreateCream(cream);
TempData["message"] = string.Format("Игрок {0} сохранен", cream.Name);
return RedirectToAction("Index", "Home");
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
}
}
ViewBag.ChoosingCreamType = GetCreamSelectList();
return View(cream);
}
when I call
public void CreateCream(CreamModel newCream)
{
if (newCream.Id == 0)
{
context.CreamModels.Add(newCream);
context.SaveChanges();
}
}
when I call context.SaveChanges() the code fails and I go to View, instead of redirect! I don't understand why it doesn't work? If i delete SaveChanges() it executes, but doesn't save in database.
my model
public class CreamModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string ImageName { get; set; }
public int? CreamTypeModel_id { get; set; }
public CreamTypeModel CreamTypeModel { get; set; }
}
error message
SqlException: The column name 'CreamTypeModel_id' is specified more
than once in the SET clause or column list of an INSERT. A column
cannot be assigned more than one value in the same clause. Modify the
clause to make sure that a column is updated only once. If this
statement updates or inserts columns into a view, column aliasing can
conceal the duplication in your code.
The issue that comes to mind is that you have a relationship without associating the FK:
public class CreamModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string ImageName { get; set; }
[ForeignKey("CreamTypeModel")]
public int? CreamTypeModel_id { get; set; }
public virtual CreamTypeModel CreamTypeModel { get; set; }
}
This links up the FK to the associated reference property.
Related
When i try to update or add a entity with a nested child i get this error following the put logic i put in place.
Why do i get this error: Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected)
[HttpPut("{id}")]
public async Task<IActionResult> PutCart(string id, ProductsInCart productInCart)
{
var cart = _context.Cart.FirstOrDefault(x => x.Id == id);
var product= cart.CartItems?.FirstOrDefault(x => x.Product.Id.Equals(productInCart.Product.Id));
if (product != null)
{
product.Quantity += 1;
}
else
{
cart.CartItems = new List<ProductsInCart>(){ productInCart };
}
_context.Entry(cart).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CartExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
Here are the objects:
public class Cart
{
public string Id { get; set; }
public List<ProductsInCart> CartItems { get; set; }
}
public class ProductsInCart
{
public int Id { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
public int Quantity { get; set; }
}
public class Product
{
public int Id { get; set; }
[Required, StringLength(100)]
public string ProductName { get; set; }
[Required, StringLength(10000)]
public string Description { get; set; }
public string ImagePath { get; set; }
public double? UnitPrice { get; set; }
public int? CategoryID { get; set; }
public virtual Category Category { get; set; }
public List<Review> Reviews { get; set; }
}
This error usually occurs when there is a change in the properties of
a record in the current version and its database version.
According to the code, the only value changed is the Quantity property.
Check this property in the ProductsInCart table.
Also if you use ef type cnfiguration, check if quantity is not contribute in concurrency check.
Concurrency check with fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductsInCart>()
.Property(p => p.Quantity)
.IsConcurrencyToken();
}
and data annotation
[ConcurrencyCheck]
public string Quantity{ get; set; }
Also, maybe concurrency check is enabled on other feild
I have created a many to many relationship using ef-core, and it definitely works, I am able to create each item in the database, and create a relationship. But the way I am doing it is a bit verbose. There also seems to be erroneous properties on the object in intellisense (I'll explain more below). I'm wondering if there is a different way.
Here are my entities
DisplayItem.cs
public class DisplayItem
{
[Key]
public int ItemId { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string FileType { get; set; }
[Required]
public string FileName { get; set; }
[Required]
public byte[] Item { get; set; }
public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}
DisplayGroup.cs
public class DisplayGroup
{
[Key]
public int GroupId { get; set; }
public string Description { get; set; }
[Required]
public string Name { get; set; }
public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}
Relationship Entity
LookUpGroupItem.cs
///naming convention will be each entity in the relationship following LookUp
public class LookUpGroupItem
{
public int ItemId { get; set; }
public DisplayItem DisplayItem { get; set; }
public int GroupId { get; set; }
public DisplayGroup DisplayGroup { get; set; }
}
Assume the model building logic is correct.
Here is the code I use to create each and then create the lookup relation.
DisplayLookUpInteraction.cs
public void Create(DisplayGroup g, DisplayItem d)
{
using (var transaction = _dataContext.Database.BeginTransaction())
try
{
_dataContext.Add(d);
_dataContext.Add(g);
_dataContext.SaveChanges();
LookUpGroupItem l = new LookUpGroupItem() { GroupId = g.GroupId, ItemId = d.ItemId };
_dataContext.Add(l);
_dataContext.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
}
}
This works fine, but again it seems a bit much. And now, whenever I work with DisplayItem or DisplayGroup there is always a property for the ICollection<LookUpGroupItem> within the object. What is this for?
You can simply do it like this:
try
{
LookUpGroupItem l = new LookUpGroupItem
{
DisplayGroup = g,
DisplayItem = d
};
_dataContext.Add(l);
_dataContext.SaveChanges();
}
catch (Exception ex)
{
// handle the error
}
When the SaveChanges method is executed with success then ItemId and GroupId properties will be configured with the correct values for you by EF.
You don't need a transaction because the single SaveChanges call will automatically trigger a transaction that will rollback all modifications if error is encountered.
I have a Model like this
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<string> SolvedBy { get; set; }
}
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
}
and then Controller like this. But I cannot update the List "SolvedBy", the next time I step through with the debugger, the list is still empty.
[HttpPost]
public string Index(string flag = "", int id=0)
{
Challenge challenge = db.Challenges.Find(id);
if (flag == challenge.Flag)
{
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<string>();
}
chall.SolvedBy.Add(User.Identity.Name);
db.Entry(chall).State = EntityState.Modified;
db.SaveChanges();
//congrats, you solved the puzzle
return "got it";
}
else
{
return "fail";
}
}
is there any way around it to make a list of strings kept in the database?
EF don't know how to store an array in database table so it just ignore it. You can create another table/entity or use XML/JSON to store the list. You can serialize the list before saving and deserialize it after loading from database
A List<T> in a model would normally map to a second table, but in your DbContext you only have a single table. Try adding a second table.
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
public DbSet<Solution> Solutions {get; set;}
}
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<Solution> SolvedBy { get; set; }
}
public class Solution
{
public int ID { get; set; }
public string Name { get; set; }
}
Then your controller can use code along the lines of...
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<Solution>();
}
chall.SolvedBy.Add(new Solution {Name=User.Identity.Name});
None of the above has been tested and I may have made some mistakes there, but the general principle I want to illustrate is the fact that you need another table. The List<T> represents a JOIN in SQL.
I am using the WebAPI in MVC4. I have a simple form which submits data to the API via a PUT request. The data arrives and is serialized fine and everything looks wonderful, except that anywhere there is a foreign key is not getting updated. And yes, the foreign key exists.
I have the following classes:
public class TriageRecord
{
[Key]
public Int64 PKEY { get; set; }
public DateTime DateAdded { get; set; }
public DateTime DateUpdated { get; set; }
public Int64? RecordSource_Id { get; set; }
public Int64? AssignedVolunteer_Id { get; set; }
public string FacebookID { get; set; }
public Int64? VANID { get; set; }
public Int64? NationBuilderID { get; set; }
public DateTime? DateVanSubmitted { get; set; }
public Int64? FollowUpLevel_Id { get; set; }
public bool? Complete { get; set; }
public string First { get; set; }
public string Mid { get; set; }
public string Last { get; set; }
public string Email { get; set; }
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip5 { get; set; }
public string HomePhone { get; set; }
public string CellPhone { get; set; }
public Int32? EmployeeStatus { get; set; }
public string StoreNumber { get; set; }
public virtual WorkArea WorkArea { get; set; } //put your focus here
public virtual Department Department { get; set; }
public virtual Shift Shift { get; set; }
}
Here is the WorkArea class which I am updating via my form.
public class WorkArea
{
[Key]
public Int64 Id { get; set; }
[StringLength(200)]
public string WorkAreaName { get; set; }
}
Here is the JSON I am posting which arrives very nicely to my API Controller:
var saveRecord = function() {
var record = {
"PKEY": $("#PKEY").val(),
"DateAdded": $("#DateAdded").val(),
"DateUpdated": "#DateTime.Now.ToString()",
"First": $("#First").val(),
"Mid": $("#Mid").val(),
"Last": $("#Last").val(),
"RecordSource_Id": $("#RecordSource_Id").val(),
"AssignedVolunteer_Id": $("#AssignedVolunteer_Id").val(),
"FacebookID": $("#FacebookID").val(),
"VANID": $("#VANID").val(),
"NationBuilderID": $("#NationBuilderID").val(),
"DateVanSubmitted": Date.now(),
"FollowUpLevel_Id": $("#FollowUpLevel_Id").val(),
"Complete": $("#Complete").val(),
"Email": $("#Email").val(),
"Address1": $("#Address1").val(),
"City": $("#City").val(),
"State": $("#State").val(),
"Zip5": $("#Zip5").val(),
"HomePhone": $("#HomePhone").val(),
"CellPhone": $("#CellPhone").val(),
"EmployeeStatus": $("#EmployeeStatus").val(),
"StoreNumber": $("#StoreNumber").val(),
"WorkArea": {
"Id": 1,
"WorkAreaName": "GM"
},
"Department": $("#Department").val(),
"Shift": $("#Shift").val()
};
It is serialized and it arrives to my controller where I can see when I set my breakpoint that the WorkArea (Models.WorkArea) is populated with Id = 1 & WorkAreaName = "GM"
So this is my controller:
public HttpResponseMessage PutTriageRecord(long id, TriageRecord triagerecord)
{
if (id == triagerecord.PKEY) //breakpoint here shows triagerecord contains workarea values
{
db.Entry(triagerecord).State = EntityState.Modified;
try
{
db.SaveChanges(); //changes get saved for everything (ie, first name) but not workarea...
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Everything is updated in my TriageRecord except for WorkArea... Why?
Update: NOT WORKING. This is what I added to my controller... it just keeps creating a ton of entires in the WorkAreas table.
public HttpResponseMessage PutTriageRecord(long id, TriageRecord triagerecord)
{
if (id == triagerecord.PKEY) //breakpoint here shows triagerecord contains workarea values
{
db.Entry(triagerecord).State = EntityState.Modified;
try
{
db.TriageRecords.Attach(triagerecord); //attach
db.Entry(triagerecord.WorkArea).State = EntityState.Added; //add
db.SaveChanges(); //WORKS!
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
You need to call Attach and set the state of the child objects to Added. It doesn't have anything to do with WebApi, this is how EF works when you are working with detached objects (e.g. when you send them out to client side and back). The db context track's the changes so it won't know what has been added modified automatically when you work with detached objects.
"If you don't call Attach the children stay detached until SaveChanges
is called where EF will assume that they are new entities (because they
are not attached to the context) and set their state to Added. Then they
will be inserted into the database."
EF 4.3 CF does not update relationships when saving changes
i have a class customer. in which i am trying to load data from the access db database.
Customer class structure is below:
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public string CustAddress { get; set; }
public string PnoneNo { get; set; }
public string MobileNo { get; set; }
public string CstNo { get; set; }
public string DlNo { get; set; }
public decimal BalAmt { get; set; }
}
and my table structure in db is as below:
now when i am trying to load data in customer class it is throwing an error:
"Specified cast is not valid."
for loading data in to class i am using below code:
public static List<Customer> LoadListItems(string strTable, string strOrderBy)
{
List<Customer> lstCustomer=null;
try
{
DataUtility objDataUtility = new DataUtility();
DataTable objCustomerList = objDataUtility.LoadCustomerInfo(strTable, strOrderBy);
lstCustomer= objCustomerList.AsEnumerable().Select(row =>
new Customer
{
CustomerId = row.Field<int>("CID"), //throwing error for this line
CustomerName = row.Field<string>("salPNm"),
CustAddress = row.Field<string>("cadd"),
MobileNo = row.Field<string>("cmbl"),
PnoneNo = row.Field<string>("cph"),
DlNo = row.Field<string>("cDlN"),
CstNo = row.Field<string>("cTin"),
BalAmt = row.Field<decimal>("cobal")
}).ToList();
}
catch (Exception ex)
{
throw ex;
}
return lstCustomer;
}
In above method CustomerId = row.Field<int>("CID"), is throwing an error coz when i commented this line it is working fine.
Please help me how can i get the int values from iennumrable list.
Thanks in Advance.
Eshwer
Replace it with -
CustomerId = Convert.ToInt64(row.Field<int>("CID"));
Also, check the value by applying Quick Watch over this line - row.Field<int>("CID"). See if it's not null and what's the value its returning.
Try this
public class Customer
{
public Int64 CustomerId { get; set; }
public string CustomerName { get; set; }
public string CustAddress { get; set; }
public string PnoneNo { get; set; }
public string MobileNo { get; set; }
public string CstNo { get; set; }
public string DlNo { get; set; }
public decimal BalAmt { get; set; }
}
and
CustomerId = row.Field<Int64>("CID")
I think that your identity is a long integer.