C# SetValues, EntityState.Modified not working - c#

I have the following entries in my database:
MeetingID AgendaItem LegistarID Title
48620 3 60710 Comment
48620 5 60615 Extending report date
48620 6 60714 Update on Additional meeting dates
48620 7 59909 Budget Rules & Procedures
48620 8 60703 Update Director name
That I need to update with these values:
MeetingID AgendaItem LegistarID Title
48620 3 60710 Public Comment
48620 5 60769 Briefing by Victor
48620 6 60615 Extending report dates
48620 7 60714 Update on Additional meeting dates
48620 8 60703 Update from Director on new processes
The way I am trying doing this in C#, is as follows:
if (ModelState.IsValid)
{
var errors = new List<string>();
var rowCounter = 1;
using (Entities db = new Entities())
{
foreach (var i in meeting)
{
if (i.MeetingID == 0)
{
// Let the user know this row is bad
errors.Add($"Row {rowCounter}: Missing Meeting ID value. " +
"Verify that the data you are trying to upload meets the required criteria, " +
"and then try to upload your file again." );
break;
}
// Check if LegistarID is missing
if (i.LegistarID == 0)
{
// Check if Agenda Item is present
if (i.AgendaItem == 0)
{
errors.Add($"Row {rowCounter}: Meeting has no LegistarID and no Agenda Item. Please check data.");
break;
}
else
{
i.LegistarID = i.AgendaItem;
}
}
var compositeKey = db.Meeting.Find(i.MeetingID, i.AgendaItem);
if (compositeKey == null)
{
// Add new
db.Meeting.Add(i);
}
else
{
// Serves as an update, or addition of a previously imported dataset
db.Entry(compositeKey).CurrentValues.SetValues(i.MeetingID);
db.Entry(compositeKey).CurrentValues.SetValues(i.AgendaItem);
db.Entry(compositeKey).CurrentValues.SetValues(i.LegistarID);
db.Entry(compositeKey).CurrentValues.SetValues(i.Title);
db.Entry(compositeKey).State = EntityState.Modified;
}
rowCounter++;
}
// If there are errors do not save and return error message
if (errors.Count > 0)
{
return new JsonResult { Data = new { status = false, message = string.Join("\n", errors) } };
}
db.SaveChanges();
status = true;
}
}
else
{
message = string.Format(#"Please, verify that the file you are trying to upload is correctly formatted,
and that the data it contains, meets the expected criteria,
then click the upload button again. \n Thank you!");
return new JsonResult { Data = new { status = status, message = message } };
}
The code for the Add part works well, but the part that updates the record if the composite key is found does not work, the update is not working.
I am not sure if I am doing this the best way, but if there is a better way I am open to change the code, or if I have an error on how I am doing the process, please let me know
Any help is appreciated.
Thank you,
Erasmo

Remove all your calls to SetValues and replace them with single one:
db.Entry(compositeKey).CurrentValues.SetValues(i);
SetValues which accepts object as parameter copies data to entity based on object properties names:
Any property on the object with a name that matches a property name in the entity type and can be read will be copied. Other properties will be ignored.

Related

Xamarin.Android SQLite SQLiteConnection.Insert does not return inserted object's ID

I'm very new to Xamarin and C# and trying to insert some sample data to my simple recipe database for testing purpourses.
However, when inserting a new recipe, the SQLiteConnection.Insert(data) does not return the ID of the inserted record as it should (see here), but instead returns 1 every time.
My insert data method:
public static int insertUpdate(Object data) {
string path = DB.pathToDatabase ();
DB.createDatabase ();
try {
var db = new SQLiteConnection(path);
int inserted = db.Insert(data);
if (inserted != 0)
inserted = db.Update(data);
return inserted;
}
catch (SQLiteException ex) {
return -1;
}
}
Inserting the sample data:
int stand = insertUpdate (new Recipe {
Name = "Standard",
Author = "Aeropress Nerd",
Description = "The perfect recipe for your first cup",
Type = "Default"
});
insertUpdate (new Step { Action = "Pour", Duration = 10, Recipe = stand });
insertUpdate (new Step { Action = "Stir", Duration = 20, Recipe = stand });
insertUpdate (new Step { Action = "Steep", Duration = 15, Recipe = stand });
int inv = insertUpdate (new Recipe {
Name = "Inverted",
Author = "Aeropress Nerd",
Description = "A more advanced brew using the inverted method.",
Type = "Default"
});
I cannot figure out what I am doing wrong. Thanks in advance for any help and sorry for the (probably) stupid question.
However, when inserting a new recipe, the SQLiteConnection.Insert(data) does not return the ID of the inserted record as it should (see here), but instead returns 1 every time.
I am pretty sure you downloaded some nuget package or component to include SQLite functionality to your Xamarin.Android App and it may be slightly different from the native implementation. Then, you should refer to the specific documentation for whatever it is that you're using on Xamarin.
My wild guess is that you are using this component. If I'm wrong, please comment to correct my answer. If I'm right, you should try this:
The object you want to insert
class Row
{
public int Id { get; set; }
}
class Recipe : Row
{
//Recipe's properties
}
class Step : Row
{
//Step's properties
}
The insertUpdate method definition
public static int insertUpdate(Row data) {
string path = DB.pathToDatabase ();
DB.createDatabase ();
try {
var db = new SQLiteConnection(path);
int inserted = db.Insert(data); //will be 1 if successful
if (inserted > 0)
return data.Id; //Acording to the documentation for the SQLIte component, the Insert method updates the id by reference
return inserted;
}
catch (SQLiteException ex) {
return -1;
}
}
The insertUpdate method usage
//As Step and Recipe both are derived classed from Row, you should be able to use insertUpdate indistinctively and without casting
insertUpdate (new Step { Action = "Steep", Duration = 15, Recipe = stand });
int inv = insertUpdate (new Recipe {
Name = "Inverted",
Author = "Aeropress Nerd",
Description = "A more advanced brew using the inverted method.",
Type = "Default"
});
why after inserting data to your db, you starting to update it by the same object right away. This code most likely redundant.
if (inserted != 0)
inserted = db.Update(data);
return inserted;

How to check existing record field and compare with post data before change ?

I am very new to MVC and hope someone can assist me.
I have a controller method to save post back data from a form. It has a field called OrderStatus. If the order status value is "Received" then only I want to execute a block of code.
What I am doing in this code is, read the post values and read the EF data again using Find and compare the values. All seems ok but when I try to save the record, it gives me below error.
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
I do understand the problem but how can I check the existing values in the database and compare and save.
My code is below
// POST: /Purchasing/Edit/5
[HttpPost]
public ActionResult Edit(PurchaseMaster purchasemaster)
{
if (ModelState.IsValid)
{
if (purchasemaster.OrderStatus == "Received")
{
string myId = purchasemaster.PurchaseId;
//check if the existing status is already set as Received or not
PurchaseMaster pm = db.PurchaseMasters.Find(myId);
if (pm.OrderStatus != "Received") //this will prevent duplicate stock updates
{
//load the items and loop through to update the stock
List<PurchaseDetail> purchasedetails = db.PurchaseDetails.Where(x => x.PurchaseId == myId).ToList();
foreach (PurchaseDetail singleitem in purchasedetails)
{
string itemcode = singleitem.ItemCode;
Item item = db.Items.Find(itemcode);
item.QtyInHand = item.QtyInHand + singleitem.Quantity;
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
}
}
}
db.Entry(purchasemaster).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchasemaster);
}
Try this it should work
//don't get this object from database
//PurchaseMaster pm = db.PurchaseMasters.Find(myId);
if (db.PurchaseMasters.Any(x =>x.Id == myId && x.OrderStatus != "Received") {
// Do your stuff
}

Add to exisiting Database instance from MVC Controller

Private BookingDB db = new BookingDB();
Private MonthDb mdb = new MonthDB();
if (ModelState.IsValid)
{
String date = (booking.Start_Date).ToString();
var check = from b in mdb.months
where b.BookedDays.Contains(date)
select b;
if (check != null)
{
return View(booking);
}
else
{
booking.Reservation_Owner = User.Identity.Name;
//Add booking.Start_Date to mdb.Entry(check).BookedDays
mdb.SaveChanges();
db.bookings.Add(booking);
db.SaveChanges();
return RedirectToAction("Index");
}
}
I've got this code that on creation of a new booking, will check that no exisiting bookings have already been made on or around that specific day.
if the day to be booked is not already been booked (ie exists under BookedDays in mdb.months) then i wish to add the Start_Date of the booking, to the BookedDays string in the mdb.months database (the mdb.month database is just a list of the 12 months)
at first i tried using mdb.Entry() to add to that specific month instance, however i cannot get it to work.
the error is:
the model does not have a definition for BookedDays
what do?
Your checking that check is null
if (check != null)
{
return View(booking);
}
else
{
and then using check anyway:
check.BookedDays
check is null and therefore does not contain any BookedDays. I'm guessing your null check is the wrong way around and should be
if (check == null)
{
return View(booking);
}
else
{
That said your problem is not well explained so I'm not sure.

Entity framework inserts wrong entity into db on savechanges

I am trying to write a program to scan a directory containing tv show folders, look up some details about the shows using tvrage API and then save the details to a database using entity framework.
My TVShow table pkey is the same value as taken from the tvrage database show id, and I am having issues when duplicate or similar folder names are returning the same Show info. In a situation where I have a directory containing three folders, "Alias", "Alias 1" , "Band of Brothers" I get the following output from my code
* TV SHOWS *
Alias....... NO MATCH......ADDING........DONE
Alias 1 ...... NO MATCH.....ADDING....CANT ADD, ID ALREADY EXISTS IN DB
Band of Brothers ...... NO MATCH..ADDING....
Before getting an UpdateException on the context.SaveChanges(); line
Violation of PRIMARY KEY constraint 'PK_TVShows'.
I can see using SQL profiler that the problem is that my app is trying to perform an insert on the alias show for a second time with duplicate key, but I can't see why. When I step through the code on the second interaction of the foreach loop (second "alias" folder), the code to save the show entity to the database is bypassed.
It is only on the next iteration of the foreach loop when I have created a new TVShow entity for "Band of Brothers" do I
actually reach the code which adds a Tvshow to context and saves, at which point the app crashes. In visual studio I can see
at the point of the crash that;
"show" entity in context.TVShows.AddObject(show) is "Band of Brothers" w/ a unique ID
context.TVShows only contains one record, the first Alias Entity
But SQL profiler shows that EntityFramework is instead inserting Alias for a second time, and I am stumped by why this is
private void ScanForTVShowFolders( GenreDirectoryInfo drive ) {
IEnumerable<DirectoryInfo> shows = drive.DirInfo.EnumerateDirectories();
foreach (DirectoryInfo d in shows) {
//showList contains a list of existing TV show names previously queried out of DB
if (showList.Contains(d.Name)) {
System.Console.WriteLine(d.Name + ".....MATCH");
} else {
System.Console.Write(d.Name + "......NO MATCH..ADDING....");
TVShow show = LookUpShowOnline(d.Name, drive.GenreName);
if (show.Id == -1) { // id of -1 means online search failed
System.Console.Write("..........CANT FIND SHOW" + Environment.NewLine);
} else if (context.TVShows.Any(a => a.Id == show.Id)) { //catch duplicate primary key insert
System.Console.Write(".......CANT ADD, ID ALREADY EXISTS IN DB" + Environment.NewLine);
} else {
context.TVShows.AddObject(show);
context.SaveChanges();
System.Console.Write("....DONE" + Environment.NewLine);
}
}
}
private TVShow LookUpShowOnline( string name, string genre ) {
string xmlPath = String.Format("http://services.tvrage.com/feeds/search.php?show='{0}'", name);
TVShow aShow = new TVShow();
aShow.Id = -1; // -1 = Can't find
XmlDocument xmlResp = new XmlDocument();
try { xmlResp.Load(xmlPath); } catch (WebException e) { System.Console.WriteLine(e); }
XmlNode root = xmlResp.FirstChild;
if (root.NodeType == XmlNodeType.XmlDeclaration) { root = root.NextSibling; }
XmlNode tvShowXML;
//if (showXML["episode"] == null)
// return false;
tvShowXML = root["show"];
if (tvShowXML != null) {
aShow.Id = System.Convert.ToInt16(tvShowXML["showid"].InnerText);
aShow.Name = tvShowXML["name"].InnerText.Trim();
aShow.StartYear = tvShowXML["started"].InnerText.Trim();
aShow.Status = tvShowXML["status"].InnerText.Trim();
aShow.TVGenre = context.TVGenres.Where(b => b.Name.Trim() == genre).Single();
}
return aShow;
}
}
Edit
Doing some more reading I added context.ObjectStateManager to my debug watchlist and I can see everytime I create a new TVShow entity a new record is added to _addedEntityStore. Actually if I remove context.TVShows.AddObject(show) the code still updates the database so manually adding to the context seems redundant.
If your are inserting object by foreach loop > better to keep the Primary Key outside and make it increment!
eg: int newID= Shows.Select(d=>d.Id).Max();
foreach(............)
{
show.Id = newID++;
.
.
. //remaining fields
.
context.TVShows.AddObject(show);
}
context.SaveChanges();
it works for me...!!
Turns out context.TVShows.AddObject(show) is unnecessary in my case, I was inadvertently adding all created show entities to the context when this query runs
aShow.TVGenre = context.TVGenres.Where(b => b.Name.Trim() == genre).Single();
This is not what I wanted, I just wanted to create the object, then decide whether to add it. Will be pretty easy to fix now I know why it's happening.

SQLite exception deleting row using sqlite-net

I got tired to search so here it goes my first SO question hoping someone had the same problem and can help me
Goal
I am trying to store my application data with a SQLite database
Application description
Windows 8 app C# XAML with local SQLite database using SQLite for Windows Runtime Extension and sqlite-net library
Table definition
public class Product {
private int _id;
[SQLite.PrimaryKey, SQLite.AutoIncrement]
public int ID
{
get { return _id; }
set { _id = value; }
}
private string _date;
public string DATE
{
get { return _date; }
set { _date = value; }
}
private string _desc;
public string DESC
{
get { return _desc; }
set { _desc = value; }
}
}
Problem1
public int Insert (object obj) description says the following:
Inserts the given object and retrieves its auto incremented primary key if it has one.
However everytime I insert a row it return 1. I can sucessfully insert with a auto-incremet ID but somehow it does not return me its ID. Why?
Problem 2
I can insert new rows but not delete them
Working around problem 1 to get last row generated ID, I try to delete rows but with no success.
See this example test that always fails:
using (var db = new SQLiteConnection(Path.Combine(_path, _dbname)))
{
var p1 = new Product() { DESC = "insert1", DATE = DateTime.Now.ToString() };
db.Insert(p1);
p1.ID = 1;
var p2 = new Product() { DESC = "insert2", DATE = DateTime.Now.ToString() };
// I am sure that this row is getting ID 2, so it will not have a wrong ID
p2.ID = 2;
db.Insert(p2);
var p3 = new Product() { DESC = "insert3", DATE = DateTime.Now.ToString() };
db.Insert(p3);
p3.ID = 3;
db.Delete<Product>(p2);
}
As you can see I try to insert 3 rows and delete the second one. The rows are inserted but I get the following SQLite.SQLiteException exception:
unable to close due to unfinalized statements or unfinished backups
Why? I don't open other connections before and after that.
Thanks in advance
Solved
Problem 1
+1 and thanks for #Bridgey for pointing out that function does not match it description and for the relevant search
The function does not return ID as it says but it defines the object ID. So when I insert a new Product, Product.ID will have last inserted ID.
Problem 2
I changed db.Delete<Product>(p2); to db.Delete(p2); and now it works. SQLite-net correctly identify the row as Product. I still don't know why the unable to close due to unfinalized statements or unfinished backups exception was happening. If someone knows why tell me please.
I think for problem 2, the issue is that you are passing the Product object as the parameter for the Delete method. The documentation says: Deletes the object with the specified primary key. I think the following should work:
db.Delete<Product>(p1.ID);
Regarding problem 1, the code of the Insert method of the sqlite-net package ends:
var count = insertCmd.ExecuteNonQuery (vals);
if (map.HasAutoIncPK) {
var id = SQLite3.LastInsertRowid (Handle);
map.SetAutoIncPK (obj, id);
}
return count;
As you can see, count is returned, even if id is set.
EDIT: Actually, according to the author this is deliberate.
"Insert returns the number of rows modified. The auto incremented columns are stored in the object. Please see the doc comments."
https://github.com/praeclarum/sqlite-net/issues/37

Categories

Resources