C# Help to validate table data before updating - c#

I am working on a C# method that receives a json object containing the following values.
meetingid
o_agendaitem
o_legistarid
o_title
n_agendaitem
n_legistarid
n_title
The values with an "o" represent the record that comes from the database.
The values with an "n" represent the edited values from a web form.
I put all together to pass it to the C# method:
var data = JSON.stringify({
'meetingid': $scope.meetingId,
'o_agendaitem': o_xref.AgendaItem,
'o_legistarid': o_xref.LegistarID,
'o_title': o_xref.Title,
'n_agendaitem': n_xref.AgendaItem,
'n_legistarid': n_xref.LegistarID,
'n_title': n_xref.Title,
I am having trouble writing the logic in a clear and simple way.
For example, lets say I have the data below:
44841 1 62704 Title#1
44841 2 62218 Title#2
44841 3 62663 Title#3
44841 4 62679 Title#4
44841 5 62709 Title#5
The user edits the values, and changes the 1 for a 6, then the data would change to:
44841 2 62218 Title#2
44841 3 62663 Title#3
44841 4 62679 Title#4
44841 5 62709 Title#5
44841 6 62704 Title#6
Meeting Id and Agenda Item are a Composite Key.
I use the "o" agenda item value and meeting id, to find that record, and if exists, delete it, and then create a new entry with the "n" values.
But I need to check that the meeting id + the "n" agenda item don't exist already too.
Example:
44841 1 62704 Title#1
44841 2 62218 Title#2 < I edit this row, I want to change the 2 for a 6, but I made a mistake and instead entered a 5, then based on my current logic, I would be deleting a record I did not mean to delete. How can I add a new check to make sure that the user is made aware of this error?
44841 3 62663 Title#3
44841 4 62679 Title#4
44841 5 62709 Title#5
This is my attempt to accomplish so far (I added notes in the parts I am not sure if I am doing it right or not):
public ActionResult UpdateXRefItem(Parameters data)
{
bool status = false;
string message = "";
using (Entities entities = new Entities())
{
//use the "o" value to find the record
var oldRec = entities.XRef_WIP.Find(data.meetingId, data.o_agendaItem);
if (oldRec != null) {
//I am not sure if I should remove the record here, or better do a few more checks.
//entities.XRef_WIP.Remove(oldRec);
//use the "n" value combined with the meeting id to see if already exists
var newVal = entities.XRef_WIP.Find(data.meetingId, data.n_agendaItem);
//if the value from the form already exists, return a message to the user
if (newVal != null)
{
status = false;
message = "This Agenda Item already exists for this Cross-Reference List. Please verfy your data and try again.";
}
else{
//after removing the "old" record, and check that the combination of meeting id + agenda item
//do not exist alreay, then create a new table entry.
//I cannot update agenda item because it is part of the composite key.
//entities.XRef_WIP.Add(new XRefWIP
//{
// MeetingID = data.meetingId,
// AgendaItem = data.n_agendaItem,
// LegistarID = data.n_legistarId,
// Title = data.n_title
//});
//status = true;
//message = "Cross-Reference record has been succssfully updated.";
//entities.SaveChanges();
}
}
}
return new JsonResult { Data = new { status = status, message = message } };
}
I hope this makes sens and someone is willing to offer a hand to complete the logic. I think I am close, but I am having trouble putting my ideas together.
Thank you,
Erasmo

See if the solution works, the idea being that you check if the record you want to add exists and if it does you stop the user from proceeding.
public ActionResult UpdateXRefItem(Parameters data)
{
using (Entities entities = new Entities())
{
// First check if the item you are trying to add exists
var currentRec = entities.XRef_WIP.Find(data.meetingId, data.n_agendaItem);
// Stop the user from continueing with the transaction
if (currentRec != null)
return new JsonResult { Data = new { status = false, message = "Record already exists." } };
// Use the "o" value to find the record
var oldRec = entities.XRef_WIP.Find(data.meetingId, data.o_agendaItem);
// If it exists then delete it
if (oldRec != null) {
entities.XRef_WIP.Remove(oldRec);
// Add new record
entities.XRef_WIP.Add(new XRefWIP()
{
MeetingID = data.meetingId,
AgendaItem = data.n_agendaItem,
LegistarID = data.n_legistarId,
Title = data.n_title
});
// Return a new result
return new JsonResult { Data = new { status = true, message = "Success!" } };
}

Related

LINQ query continues to return null after 1st successful call

I have an upload function in my ASP.NET MVC web application which allows a user to upload a document. The system then uses various OCR APIs to read data from the uploaded document, this data is then saved in the "SecondarySchoolSurvey" table in the database. I have a LINQ query in the upload code which finds a row in the database "SecondarySchoolSurvey" table that matches the Id passed in. Various fields in this row are then updated and saved back into the database.
Now this upload function works fine first time. Although if I try to use the upload function again (without restarting the IIS server) then it throws the following error Object reference not set to an instance of an object.
After setting various break points I noticed that the LINQ query: var s1 = db.SecondarySchoolSurveys.FirstOrDefault(s => s.Id == id); returns null the 2nd time it is called i.e. it does not find the record which matches the Id passed in, even though it exists in the database.
So for now, I need to reset the web app IIS server after every time I want to test the upload function. Otherwise this LINQ query with return null the 2nd time I try upload a document. Has anybody seen anything like this before? I'm not sure how useful it will be providing code but here is one of the class, the one which contains the LINQ query:
public class SurveyCheckboxAnswers
{
private RDSContext db = new RDSContext();
//Adds Question 2 answer
public void AddQ2Answer(SurveyCheckboxCollections checkboxes, int id)
{
//find Survey record in db which matches id in order to update with checkbox data
var s1 = db.SecondarySchoolSurveys.FirstOrDefault(s => s.Id == id);
CheckboxData q2Male = checkboxes.SecondarySchoolCheckboxes["Q2Male"];
CheckboxData q2Female = checkboxes.SecondarySchoolCheckboxes["Q2Female"];
CheckboxData q2Other = checkboxes.SecondarySchoolCheckboxes["Q2Other"];
CheckboxData q2DontWantToSay = checkboxes.SecondarySchoolCheckboxes["Q2DontWantToSay"];
//numbers of checkboxes marked for validation
int checkboxValidaiton = 0;
//update SecondarySchoolSurvey checkbox answers in database with IsChecked values from checkbox dictionary
if (q2Male.IsChecked)
{
s1.Q2 = Gender.Male;
checkboxValidaiton++;
}
if (q2Female.IsChecked)
{
s1.Q2 = Gender.Female;
checkboxValidaiton++;
}
if (q2Other.IsChecked)
{
s1.Q2 = Gender.Other;
checkboxValidaiton++;
}
if (q2DontWantToSay.IsChecked)
{
s1.Q2 = Gender.None;
checkboxValidaiton++;
}
//validate only 1 checkbox has been marked
if(checkboxValidaiton == 0)
{
s1.Flag = true;
s1.FlagContent += "| Question2: no checkboxes marked. ";
}
else if (checkboxValidaiton > 1)
{
s1.Flag = true;
s1.FlagContent += "| Question2: more than 1 checkboxes marked. ";
}
db.SaveChanges();
}
//Adds Question 6 answer
public void AddQ6Answer(SurveyCheckboxCollections checkboxes, int id)
{
//find Survey record in db which matches id in order to update with checkbox data
var s1 = db.SecondarySchoolSurveys.FirstOrDefault(s => s.Id == id);
CheckboxData q6Higher = checkboxes.SecondarySchoolCheckboxes["Q6Higher"];
CheckboxData q6Ordinary = checkboxes.SecondarySchoolCheckboxes["Q6Ordinary"];
CheckboxData q6Other = checkboxes.SecondarySchoolCheckboxes["Q6Other"];
//numbers of checkboxes marked for validation
int checkboxValidaiton = 0;
if (q6Higher.IsChecked)
{
s1.Q6a = MathLevel.Higher;
checkboxValidaiton++;
}
if (q6Ordinary.IsChecked)
{
s1.Q6a = MathLevel.Ordinary;
checkboxValidaiton++;
}
if (q6Other.IsChecked)
{
s1.Q6a = MathLevel.Other;
checkboxValidaiton++;
}
//validate only 1 checkbox has been marked
if (checkboxValidaiton == 0)
{
s1.Flag = true;
s1.FlagContent += "| Question6: no checkboxes marked. ";
}
else if (checkboxValidaiton > 1)
{
s1.Flag = true;
s1.FlagContent += "| Question6: more than 1 checkboxes marked. ";
}
db.SaveChanges();
}
//Adds Question 7 answer
public void AddQ7Answer(SurveyCheckboxCollections checkboxes, int id)
{
//find Survey record in db which matches id in order to update with checkbox data
var s1 = db.SecondarySchoolSurveys.FirstOrDefault(s => s.Id == id);
CheckboxData q7Physics = checkboxes.SecondarySchoolCheckboxes["Q7Physics"];
CheckboxData q7Biology = checkboxes.SecondarySchoolCheckboxes["Q7Biology"];
CheckboxData q7Chemistry = checkboxes.SecondarySchoolCheckboxes["Q7Chemistry"];
CheckboxData q7Science = checkboxes.SecondarySchoolCheckboxes["Q7Science"];
CheckboxData q7None = checkboxes.SecondarySchoolCheckboxes["Q7None"];
//numbers of checkboxes marked for validation
int checkboxValidaiton = 0;
if (q7Physics.IsChecked)
{
s1.Q7 = "Physics";
checkboxValidaiton++;
}
if (q7Biology.IsChecked)
{
s1.Q7 += "Biology";
checkboxValidaiton++;
}
if (q7Chemistry.IsChecked)
{
s1.Q7 += "Chemistry";
checkboxValidaiton++;
}
if (q7Science.IsChecked)
{
s1.Q7 += "Science Junior";
checkboxValidaiton++;
}
if (q7None.IsChecked)
{
s1.Q7 += "None";
checkboxValidaiton++;
}
//validate only 1 checkbox has been marked
if (checkboxValidaiton == 0)
{
s1.Flag = true;
s1.FlagContent += "| Question7: no checkboxes marked. ";
}
db.SaveChanges();
}
...
...
...
}
Your problem here most likely lies in the fact you are using a private variable for the db context. The appropriate way to open/close the connection to your db using a context is to use a using statement like so:
using(var db = new RDSContext())
{
//...Do work here with your context
}
Per MSDN
The lifetime of the context begins when the instance is created and ends when the instance is either disposed or garbage-collected. Use using if you want all the resources that the context controls to be disposed at the end of the block. When you use using, the compiler automatically creates a try/finally block and calls dispose in the finally block.
Since your query is running but is returning null, your db context is likely valid. That means the issue is likely the query itself. I suspect that on subsequent runs, you are passing in a different id that has no record in your database. Asking the db for the first record, if any, that matches the supplied id therefor returns null. Use a breakpoint to inspect both the provided id and the generated sql. Check in your db that you have a row for the id.
On another note, I would suggest that you use the method First or Single instead of FisrtOrDefualt if you expect the query to always return a row. This will throw an error if there are no matching rows, which guarantees that you have a not-null result returned if no error is thrown. The advantage is that after the method you now know that there was a matching row, whereas otherwise you may have to include a null check.
There is a great table on these operators here

update only one field using viewmodel in httpPost form <edited>

this code suppose to update only one value in a table and keep all the other value as is .
how ever I cant find out why , the db.SaveChanges doesn't save it . in the debugger I can see that the value has change ,
this is my code
if (model.rev.GBU == "Good")
{
//This section will do:---- 1> get subjectId -- 2> Update only in the subjectid good rank
//Update this subjectid (int)good +1 data
model.sub.SubjectId = R_getvelue.SubjectId;
//get the relevant value from the Db
var Good = R_getvelue.Good;
int iGoodRating = Convert.ToInt32(Good);
//Add 1 (one )to the value in the Db
iGoodRating++;
model.sub.Good = iGoodRating;
//Keep All the existing data in the other fields
UpdateModel(model.sub);
// db.Entry(model.sub).State = EntityState.Modified; >removing all values biside "Good
}
await db.SaveChangesAsync();
return RedirectToAction("index");
}
ViewBag.OccupationId = new SelectList(db.occupation, "OccupationId", "OccupationDecription", model.Occupation);
return View(model);
}

Inserting object twice to EF 6

How can i insert object twice in database with different value
when the object User has Code > 10 then it insert 2 object but some how EF update the value of the first inserted object.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Code { get; set; }
}
and in my action i'm saving to databse
// Post Action
uow.Users.Add(user);
uow.Commit(); // save first time
if (user.Code > 10)
{
user.Name = "NAS";
uow.Users.Add(user);
uow.Commit(); //save second time
}
My object is
User = (Name = "Mike",Code=12)
Database result is
Result Database
id =1 Name="NAS" Code=12
id =2 Name="NAS" Code=12
This is correct behavior; so why is this happening:
When you save the first time, it creates a new entry in the database using your originally created user object.
When you save the second time, it takes the original object, which for lack of a better term, has a database reference and updates the name of the object AND it also adds that same object to the collection again; this is why you are seeing the same information in the database.
Your code should be:
var user = new User { Name = "Mike", Code = 12 };
uow.Users.Add(user);
uow.Commit(); // save first time
if (user.Code > 10)
{
var newUser = new User { Name = "NAS", Code = user.Code };
uow.Users.Add(newUser);
uow.Commit(); //save second time
}
Your results should then be:
Result Database
id = 1 Name = "Mike" Code = 12
id = 2 Name = "NAS" Code = 12
drneel gave a good explanation on why your code does not work as you intended.
Now maybe there is a workaround for what you want to achieve: inserting two distinct entities in DB from a single instance. Detach it from context, change what needs to be changed, re-add it.
If your uow is inheriting from DbContext, it would be:
var user = new User { Name = "Mike", Code = 12 };
uow.Users.Add(user);
uow.Commit(); // save first time
if (user.Code > 10)
{
uow.Entry(user).State = EntityState.Detached;
user.Id = 0;
user.Name = "NAS";
uow.Users.Add(user);
uow.Commit(); //save second time
}
I am not comfortable with the way we have to detach entities in EF. It may work better to instead dispose your uow after first 'save' and use another one for second 'save'.
The main risk concerns navigation properties. Detaching does not ends tracking on collection of other entities which may hold a reference to your user instance. Re-adding it may screw EF navigation properties bookkeeping.
(I would not fear that with NHibernate, such bookkeeping is not handled by NH. It remains the responsibility of the developer with NH.)

C# Comparing data with database by using array

My database contains 2 records. So, I retrieve these 2 records and store in array. Let's say my database have start point, destination, terminal and departure time. If user trying to add same start point, same destination, same terminal and same departure time, it will show error message. But if user trying to add same start point, same destination, same terminal but DIFFERENT departure time. It suppose to allow user add, but I having problem with user not allow to add.
Example:
These record are in my database
Record 1 : Terminal = 1 , From = US , To = UK, Time = 10.00
Record 2 : Terminal = 1 , From = US , To = AUS , Time = 2.00
When user want to add new record
User wish to add: Terminal = 1, From = US, To = AUS, Time = 10.00
It's tell me record exist because the time 10.00 exist in other record which is in different destination. Anyone can help?
if (ow_terminal.Any(s => ddlterminal.SelectedValue.Contains(s))
&& ow_depart.Any(s => ddlDeparture.SelectedValue.Contains(s))
&& ow_origin.Any(s => ddlOrigin.SelectedValue.Contains(s))
&& ow_destination.Any(s => ddlDestination.SelectedValue.Contains(s)))
{
lblMessage.Text = "Record exist";
}
else
{
lblMessage.Text = "No record exist"
}
The problem is with your Linq statement.
Think logically about what it is actually saying:
if(terminal_array.Any(s => new_terminal_record.Contains(s)))
So if ANY of the values in your terminal_array matches the new terminal record that the user entered then this statement is true, and your code will then evaluate the next line
if(depart_array.Any(s => new_depart_record.Contains(s)))
And it will do the same thing, if ANY of the values in your depart array matches the new departure record that the user entered then this statement is also true.
What you most likely want to do is to store the records in an array of whatever your record type is and do the Linq statement over each record in that array, something along the lines of:
if(records_array.Any(s => new_record.terminal.Equals(s.terminal) &&
new_record.depart.Equals(s.depart) &&
new_record.origin.Equals(s.origin) &&
new_record.destination.Equals(s.destination)))
{
// Record Exists
}
else
{
// No Record Exists
}
For a more complete example, I have replicated your implementation and then included my implementation:
class Program
{
// Example of database records
struct _dbTypes
{
public string terminal;
public string depart;
public string origin;
public string destination;
};
static void Main(string[] args)
{
// First record
_dbTypes record1;
record1.terminal = "1";
record1.depart = "10.00";
record1.origin = "US";
record1.destination = "UK";
// Second record
_dbTypes record2;
record2.terminal = "1";
record2.depart = "2.00";
record2.origin = "US";
record2.destination = "AUS";
// Your implementation has each of the records internals
// separated into their own array
List<String> terminals = new List<string>();
List<String> departs = new List<string>();
List<String> origins = new List<string>();
List<String> destination = new List<string>();
terminals.Add(record1.terminal);
terminals.Add(record2.terminal);
departs.Add(record1.depart);
departs.Add(record2.depart);
origins.Add(record1.origin);
origins.Add(record2.origin);
destination.Add(record1.destination);
destination.Add(record2.destination);
// The NEW record that the user has entered
_dbTypes record3;
record3.terminal = "1";
record3.depart = "10.00";
record3.origin = "US";
record3.destination = "AUS";
// Example of your implementation
if (terminals.Any(s => record3.terminal.Contains(s)) &&
departs.Any(s => record3.depart.Contains(s)) &&
origins.Any(s => record3.origin.Contains(s)) &&
destination.Any(s => record3.destination.Contains(s)))
{
// Will fall into here
Console.WriteLine("Record exists");
}
else
{
Console.WriteLine("No record exists");
}
// What you should probably be doing...
//
// Array of your record type
List<_dbTypes> dataBaseEntries = new List<_dbTypes>();
//
// Add the first two records as a whole record, not separated out
dataBaseEntries.Add(record1);
dataBaseEntries.Add(record2);
// Now you want to do the Linq statement over the record
// AND'ing each .Equals on the current records internal objects
if (dataBaseEntries.Any(s => record3.terminal.Equals(s.terminal) &&
record3.depart.Equals(s.depart) &&
record3.origin.Equals(s.origin) &&
record3.destination.Equals(s.destination)))
{
Console.WriteLine("Record exists");
}
else
{
Console.WriteLine("No record exists");
dataBaseEntries.Add(record3);
}
}
}
From what I can tell, you seem to be storing the information for each record in separate arrays, one each for terminal, depart, origin, and destination. This is a bit problematic since there isn't a concrete way of determining that the information belongs to the same record UNLESS for some reason, the information are ordered in the array per record:
example:
ow_terminal[0] = 1
ow_depart[0] = "US"
ow_origin[0] = "UK"
ow_destination[0] = 10.00
If this is the case, then you have to change your if statement logic. I don't think LINQ can help in this case, but you can try:
bool exists = false;
for(int i = 0; i < ow_terminal.Count; i++)
{
if(ddlterminal.SelectedValue.Contains(ow_terminal[i])
&& ddlDeparture.SelectedValue.Contains(ow_depart[i])
&& ddlOrigin.SelectedValue.Contains(ow_origin[i])
&& ddlDestination.SelectedValue.Contains(ow_destination[i]))
{
exists = true;
break;
}
}
if(exists) lblMessage.Text = "Record exist";
else lblMessage.Text = "No record exist";
The code checks the values of each array in the same position for equality. Again, this would only work if the items on all of the arrays are arranged per record. ALSO, you may want to change the equality check since Contains may not be best fit for your purpose ("US" is contained in "RUSSIA", for example.)
HOWEVER, managing four arrays and making sure that they are ordered correctly all the time is very fragile. What you want to do is to create just one array (or list) of objects that correspond to your record with corresponding properties:
public class FlightRecord
{
public int Terminal {get;set;}
public string From {get;set;}
public string To {get;set;}
public decimal Time {get;set;} // How are you storing time?
}
Create an array of FlightRecords from your database records and then you can do your logic like you wanted with a few minor changes:
if (ow_flightrecords.Any(s => ddlterminal.SelectedValue.Contains(s.Terminal.ToString()
&& ddlDeparture.SelectedValue.Contains(s.Time.ToString())
&& ddlOrigin.SelectedValue.Contains(s.From)
&& ddlDestination.SelectedValue.Contains(s.To)))
{
lblMessage.Text = "Record exist";
}
else
{
lblMessage.Text = "No record exist"
}
This one is more robust and you can use LINQ easily. Again, change how you do the comparisons since Contains may not be as good a fit for your purpose as you think it is.
And as Synchro noted, it's probably a good idea to do the check on the database directly via a stored procedure before you do the actual INSERT into the database. It will be much faster this way.

C# Updating Data Issue

I am currently using one button for inserting/updating content within a table. It then takes the uploaded CSV and inserts or updates it into a data table depending on whether the row exists or not.
Here is the code fired after the button's OnClick:
if (ExcelDDL.SelectedValue == "Time Points" && fileName == "TimePoints.csv")
{
var GetTPoints = (SEPTA_DS.TimePointsTBLDataTable)tpta.GetDataByCategory(CategoryDDL.SelectedItem.ToString());
//Loop through each row and insert into database
int i = 0;
foreach (DataRow row in TempRouteDataTable.Rows)
{
//Gather column headers
var category = Convert.ToString(CategoryDDL.SelectedItem);
var agency = Convert.ToString(row["Agency"]);
if (agency == null || agency == "")
{
//If row is empty skip it entirely
goto skipped;
}
var route = Convert.ToString(row["Route"]);
var GetRShortName = (SEPTA_DS.RoutesTBLDataTable)rta.GetDataByRouteID(route);
var newRoute = "";
if (GetRShortName.Rows.Count > 0)
{
newRoute = Convert.ToString(GetRShortName.Rows[0]["route_short_name"]);
}
var direction = Convert.ToString(row["Direction"]);
var serviceKey = Convert.ToString(row["Service Key"]);
var language = Convert.ToString(row["Language"]);
var stopID = Convert.ToString(row["Stop ID"]);
var stopName = Convert.ToString(row["Stop Name"]);
if (stopName.Contains("accessible"))
{
string[] noHTML = stopName.Split('>');
int insertH = Convert.ToInt32(hta.InsertHandicapRow(newRoute,noHTML[2]));
}
var sequence = Convert.ToString(row["Sequence"]);
var origID = -1;
if (GetTPoints.Rows.Count > 0)
{
origID = Convert.ToInt32(GetTPoints.Rows[i]["TPointsID"]);
var GetID = (SEPTA_DS.TimePointsTBLDataTable)tpta.GetDataByID(origID);
if (GetID.Rows.Count < 1)
{
origID = -1;
}
}
if (origID == -1)
{
int insertData = Convert.ToInt32(tpta.InsertTimePoints(category, agency, newRoute, direction, serviceKey, language, stopID, stopName, sequence));
}
else
{
int updateData = Convert.ToInt32(tpta.UpdateTimePoints(category, agency, newRoute, direction, serviceKey, language, stopID, stopName, sequence, origID));
}
skipped:
i++;
}
}
You can see how I check whether to insert or update around the bottom. I am using this method across other sections of this program and it works just fine. But in this case it is distorting my datatable immensely and I can't figure out why.
This is the bottom part of my table after inserting [no items currently within the database]:
This is the table after reuploading the CSV with data already existing within the table:
I am also getting this error when updating There is no row at position 2230.
What is going wrong in the code to cause this huge shift? I am just checking to see if the ID exists and if it does update rather than insert.
Also the reason i am using goto is because there are blank rows in the document that need to be skipped.
Is your TPointsID column, a auto-generated number? If so, since you are skipping the empty row, some referential integrity problem might be occuring,because of empty data in the skipped rows in the database.
From the error : There is no row at position 2230 , it is also understood that, because of the skipping you might be trying to access some non existent row in the datatable.
Also, if possible consider using the ADO.NET DataAdapter which has got the CRUD operation capability. You can find more about it at : http://support.microsoft.com/kb/308507

Categories