LinQ query, only one record submitted/inserted - c#

I'm trying to insert only males teachers into the "MaleTeachers" table, but after program is executed I see only one teacher in that table. In addition, I have more then one teacher in the "Stuffs" table, but inserted one is the last that matches "if" criteria. Could you correct this code please. Service implementation:
public void AddTeachers()
{
DataClasses1DataContext data = new DataClasses1DataContext();
DataClasses2DataContext data2 = new DataClasses2DataContext();
MaleTeacher tchr = new MaleTeacher();
foreach (var d in data.Stuffs)
{
if (d.stuffSex == true && d.stuffJob == "Teacher")
{
tchr.teacherName = d.stuffName;
tchr.teacherAge = d.stuffAge;
tchr.teacherJob = d.stuffJob;
tchr.teacherDepartm = "geology";
data2.MaleTeachers.InsertOnSubmit(tchr);
}
}
data2.SubmitChanges();
}

you foreach loop should be.
foreach (var d in data.Stuffs)
{
if (d.stuffSex == true && d.stuffJob == "Teacher")
{
MaleTeacher tchr = new MaleTeacher();
tchr.teacherName = d.stuffName;
tchr.teacherAge = d.stuffAge;
tchr.teacherJob = d.stuffJob;
tchr.teacherDepartm = "geology";
data2.MaleTeachers.InsertOnSubmit(tchr);
}
}

You are creating only one intance and then modifying it again and again... instead you should create a different MaleTeacher instance on every time.
MaleTeacher tchr = new MaleTeacher();
foreach (var d in data.Stuffs)
{
if (d.stuffSex == true && d.stuffJob == "Teacher")
{
tchr.teacherName = d.stuffName;
tchr.teacherAge = d.stuffAge;
tchr.teacherJob = d.stuffJob;
tchr.teacherDepartm = "geology";
data2.MaleTeachers.InsertOnSubmit(tchr);
tchr = new MaleTeacher();
}
}

Related

db.SaveChanges in ForEach causes 'New transaction is not allowed because there are other threads running in the session'

I have an excel file with about 21000 rows . I imported it into a temp Table in my database.
Now I want to do some conversions on my data and then put them into my main table.
When I do SaveChanges() inside a foreach I got the following error:
Microsoft.Data.SqlClient.SqlException: 'New transaction is not allowed because there are other threads running in the session
When I use it after the foreach no error occurs and the table has just 4 records inserted instead of all 21000 records that I expected.
public ActionResult FeedTempdataToMainDB()
{
var L = new Leave();
// var leaves = new List<Leave>();
foreach (var item in db.TempLeaves)
{
L.Pcode = Int32.Parse(item.Cod);
var z = int.Parse(item.LT) - 1;
if (z == 0) L.LT = Leave.LeaveType.Saati;
else L.LT = Leave.LeaveType.Roozane;
var o = int.Parse(item.DLT) - 1;
if (o == 0) L.DLT = Leave.DLType.Estehghaghi;
if (o == 1) L.DLT = Leave.DLType.Estelaji;
else L.DLT = Leave.DLType.Bihoghoogh;
L.LeaveDayStart = item.LeaveDayStart.Old6digToMiladi();
L.LeaveDayEnd = item.LeaveDayEnd.Old6digToMiladi();
L.LeaveTimeStart = StringToHour(item.LeaveTimeStart);
L.LeaveTimeEnd = StringToHour(item.LeaveTimeEnd);
L.LeaveDays = int.Parse(item.LeaveDays);
L.LeaveMinuts = SaatiLengh(item.LeaveMinuts);
L.RegDate = StringToHour(item.RegTime);
L.RegDate = item.RegDate.Old6digToMiladi().Date;
L.RegistrarCode = Int32.Parse(item.RegistrarCode);
L.HijriYear = L.LeaveDayStart.GetHijriYear();
var t = IsOk(item.RegTime);
if (L.DLT == 0 && t == false || L.LT == 0)
{
L.Calculate = false;
L.IsActive = false;
}
else { L.Calculate = true; L.IsActive = true; }
db.Leaves.Add(L);
db.SaveChangesAsync();
}
//db.SaveChanges();
return RedirectToAction("index");
You have a bug in your code. You declared and created L outside of the loop. Each time you add the same L , only with different data. In the end you have list of the same data that was created during the last foreach loop cicle.
try this:
foreach (var item in db.TempLeaves)
{
var z = int.Parse(item.LT) - 1;
var L = new Leave{
Pcode = Int32.Parse(item.Cod),
LeaveTimeStart = StringToHour(item.LeaveTimeStart),
LeaveTimeEnd = StringToHour(item.LeaveTimeEnd),
LeaveDays = int.Parse(item.LeaveDays),
LT = z == 0? Leave.LeaveType.Saati : Leave.LeaveType.Roozane
};
db.Leaves.Add(L);
}
or this
var leaves= new List<Leave>();
foreach (var item in db.TempLeaves)
{
var z = int.Parse(item.LT) - 1;
var L = new Leave{
Pcode = Int32.Parse(item.Cod),
LeaveTimeStart = StringToHour(item.LeaveTimeStart),
LeaveTimeEnd = StringToHour(item.LeaveTimeEnd),
LeaveDays = int.Parse(item.LeaveDays),
LT = z == 0? Leave.LeaveType.Saati : Leave.LeaveType.Roozane
};
leaves.Add(L);
}
if(leaves.Count>0)
{
db.Leaves.AddRange(leaves);
db.SaveChanges();
}
if you want to use async save you have to make async action at first.
The raison every time that foreach execute the savechnages there is a thread that your not note controlling. Since entity framework is managing the savechanges function. You have to execute your savechnages after the foreach or use async function.
here an example for the async:
private static async Task<Student> GetStudent()
{
Student student = null;
using (var context = new SchoolDBEntities())
{
Console.WriteLine("Start GetStudent...");
student = await (context.Students.Where(s => s.StudentID == 1).FirstOrDefaultAsync<Student>());
Console.WriteLine("Finished GetStudent...");
}
return student;
}
*This Code finally worked:
public ActionResult FeedTempdataToMainDB()
{
var leaves = new List<Leave>();
foreach (var item in db.TempLeaves)
{
var L = new Leave();
L.Pcode = Int32.Parse(item.Cod);
var z = int.Parse(item.LT) - 1;
if (z == 0) L.LT = Leave.LeaveType.Saati;
else L.LT = Leave.LeaveType.Roozane;
var o = int.Parse(item.DLT);
if (o == 0) L.DLT = Leave.DLType.Estehghaghi;
if (o == 1) L.DLT = Leave.DLType.Estelaji;
else L.DLT = Leave.DLType.Bihoghoogh;
L.LeaveDayStart = item.LeaveDayStart.Old6digToMiladi();
L.LeaveDayEnd = item.LeaveDayEnd.Old6digToMiladi();
L.LeaveTimeStart = StringToHour(item.LeaveTimeStart);
L.LeaveTimeEnd = StringToHour(item.LeaveTimeEnd);
L.LeaveDays = int.Parse(item.LeaveDays);
L.LeaveMinuts = SaatiLengh(item.LeaveMinuts);
L.RegDate = StringToHour(item.RegTime);
L.RegDate = item.RegDate.Old6digToMiladi().Date;
L.RegistrarCode = Int32.Parse(item.RegistrarCode);
L.HijriYear = L.LeaveDayStart.GetHijriYear();
var t = IsOk(item.RegTime);
if (L.DLT == 0 && t == false || L.LT == 0 && t == false)
{
L.Calculate = false;
L.IsActive = false;
}
else { L.Calculate = true; L.IsActive = true; }
leaves.Add(L);
}
if (leaves.Count > 0)
{
db.Leaves.AddRange(leaves);
db.SaveChanges();
}
return RedirectToAction("index");
}

Comparing Two Collections data with each other

I have two observable collections. 1. TruckItems 2. TruckItemsComparison. Both are exactly the same.
I load data into the first TruckItems collection from EF6, then 10 seconds later I load data into the second collection TruckItemsComparison. Now the new data that was added in my 2nd collection might have been updated lately from another source and I need to only add the latest data that does not yet exist in my first collection.
I want to check if ANY of the id's from my 2nd collection does not match any of the id's in my first collection and then only add the items that does not match.
CODE:
Here is where I load my data:
private async void LoadTrucks()
{
using (TruckServiceClient service = new TruckServiceClient())
{
var items = await service.GetTrucksAsync();
if (TruckItems.Count == 0)
{
foreach (var item in items)
{
TruckItems.Add(new TruckItems
{
TruckId = item.TruckId,
TruckQuoteId = item.QuoteId,
TruckPhaseId = item.CurrentPhaseId,
TruckChassisManufacturer = item.ChassisManufacturer,
TruckChassisModel = item.ChassisModel,
TruckStatus = item.Status,
TruckJobNumber = item.JobNumbers,
TruckAddedBy = item.AddedBy,
TruckClientName = item.ClientName,
TruckClientSurname = item.ClientSurname,
TruckClientDetail = item.ClientDetail,
TruckCurrentPhase = item.CurrentPhase
});
}
}
foreach (var item in items)
{
TruckItemsComparison.Add(new TruckItems
{
TruckId = item.TruckId,
TruckQuoteId = item.QuoteId,
TruckPhaseId = item.CurrentPhaseId,
TruckChassisManufacturer = item.ChassisManufacturer,
TruckChassisModel = item.ChassisModel,
TruckStatus = item.Status,
TruckJobNumber = item.JobNumbers,
TruckAddedBy = item.AddedBy,
TruckClientName = item.ClientName,
TruckClientSurname = item.ClientSurname,
TruckClientDetail = item.ClientDetail,
TruckCurrentPhase = item.CurrentPhase
});
}
}
}
And here is where I want to compare my two collections:
public void UpdateTrucks()
{
LoadTrucks();
if (TruckItems.Count != 0)
{
var truckItemsId = TruckItems.Where(x => x.TruckId != 0).First().TruckId;
foreach (var item in TruckItemsComparison.Where(x => x.TruckId != truckItemsId))
{
TruckItems.Add(item);
}
}
}
My problem is that it adds the data from both the two collections together, regardless if the id's correspond or not. Clearly my logic here does not work, so can anyone please show me a way of how I can compare the data and only insert id's that do not yet exist in my TruckItems collection. Thanks and please let me know if you need any more information.
You can enumerate through each of the items in your TruckItemsComparison by using Except:
public void UpdateTrucks()
{
LoadTrucks();
if (TruckItems.Count != 0)
{
foreach (var item in TruckItemsComparison.Except(TruckItems))
{
TruckItems.Add(item);
}
}
}
If all you want to do is compare the Ids of your TruckItems then you can implement your own IEqualityComparer:
internal class TruckItemsComparer : IEqualityComparer<TruckItems>
{
#region IEqualityComparer Members
public bool Equals(TruckItems x, TruckItems y)
{
return (((x == null) && (y == null)) ||
((x != null) && (y != null) && x.TruckId == y.TruckId));
}
public int GetHashCode(TruckItems obj)
{
return obj. TruckId.GetHashCode();
}
#endregion
}
And then use like so:
foreach (var item in TruckItemsComparison.Except(TruckItems, new TruckItemsComparer()))

Creating a new record resulting in creating identic new record in related table

Each time I create a new PurchasedProduct, and refer a Product to it, upon insertion to the table, the table always creates a new identical Product and refer to the new one, instead of refering to the existing one.
So, I have these 3 relevant tables :
..which means that a Purchase can have many PurchasedProduct, each to represent a purchased Product and how many of it purchased (Quantity).
This is the winform :
These are the relevant codes :
public partial class fmAddEditPurchase : Form
{
List<Product> products;
Purchase purchase;
public fmAddEditPurchase()
{
InitializeComponent();
Purchase = new Purchase();
Text = "Add New Purchase";
dtpDate.Value = DateTime.Now.Date;
RefreshPurchasedProduct();
LoadProductList();
}
private void RefreshPurchasedProduct()
{
List<PurchasedProduct> ppQuery = new List<PurchasedProduct>();
BindingSource bi = new BindingSource();
if (Purchase.PurchasedProducts.Count > 0)
{
using (var context = new dbKrunchworkContext())
{
bi.DataSource = Purchase.PurchasedProducts.
Join(products, x => x.Product, y => y, (x, y) =>
new { y.Product_Name, x.Price, x.Quantity }).
ToList();
}
}
dgvPurchasedProduct.DataSource = bi;
dgvPurchasedProduct.Refresh();
}
private void LoadProductList()
{
using (var context = new dbKrunchworkContext())
{
products = context.Products.ToList();
}
cbProductName.DataSource = products.
Select(x => x.Product_Name).ToList();
}
private void btAddProduct_Click(object sender, EventArgs e)
{
decimal price = 0.0M;
if (decimal.TryParse(tbPrice.Text, out price) && price > 0)
{
PurchasedProduct temp = Purchase.PurchasedProducts.
FirstOrDefault(
x => x.Product ==
products[cbProductName.SelectedIndex] &&
x.Price == price);
if (temp == null)
{
PurchasedProduct newPP = new PurchasedProduct();
newPP.Product = products[cbProductName.SelectedIndex];
newPP.Purchase = Purchase;
newPP.Quantity = (int)numQuantity.Value;
newPP.Price = price;
if (newPP.Product != null)
{
Purchase.PurchasedProducts.Add(newPP);
}
}
else
{
temp.Quantity += (int)numQuantity.Value;
}
RefreshPurchasedProduct();
}
}
private void btSave_Click(object sender, EventArgs e)
{
try
{
Purchase.Received_Date = dtpDate.Value;
Purchase.Total_Amount = decimal.Parse(tbTotalPrice.Text);
Purchase.Note = tbNote.Text;
using (var context = new dbKrunchworkContext())
{
for (int i = 0; i < Purchase.PurchasedProducts.Count; i++)
{
PurchasedProduct pp =
Purchase.PurchasedProducts.ElementAt(i);
Product p = context.Products.
FirstOrDefault(x => x.ID == pp.Product.ID);
pp.Product = p;
}
}
}
catch (Exception)
{
}
}
}
And this is the main form which insert the new record to the table after receiving DialogResult() == DialogResult.OK from above Form.
private void Purchase_AddNewRecord()
{
fmAddEditPurchase addForm = new fmAddEditPurchase();
if (addForm.ShowDialog() ==
DialogResult.OK && addForm.Purchase.Total_Amount > 0)
{
using (var context = new dbKrunchworkContext())
{
context.Purchases.Add(addForm.Purchase);
context.SaveChanges();
}
}
}
Example :
Before
What I did (+ save)
After (Please note that it creating a new Product instead of using the old one)
You are using multiple instance of your DbContext in multiple methods of your form.
You have this issue because the below code will take all objects into the Purshase graph and mark all of them into Added state.
private void Purchase_AddNewRecord()
{
fmAddEditPurchase addForm = new fmAddEditPurchase();
if (addForm.ShowDialog() ==
DialogResult.OK && addForm.Purchase.Total_Amount > 0)
{
using (var context = new dbKrunchworkContext())
{
context.Purchases.Add(addForm.Purchase);
context.SaveChanges();
}
}
}
To solve this you must change the state of every Product instance related to Purshase instance like the follwoing code :
private void Purchase_AddNewRecord()
{
fmAddEditPurchase addForm = new fmAddEditPurchase();
if (addForm.ShowDialog() ==
DialogResult.OK && addForm.Purchase.Total_Amount > 0)
{
using (var context = new dbKrunchworkContext())
{
context.Purchases.Add(addForm.Purchase);
foreach (var purchasedProduct in addForm.Purchase.PurchasedProducts)
{
context.Entry(purchasedProduct.Product).State = EntityState.Unchanged;
}
context.SaveChanges();
}
}
}
It is not recommended, when you are using Windows Forms or WPF, to create a new instance of your DbContext into every method like you actually do. You must create just one per form by creating a field for that.

save children along with parent linq to sql

I have two tables Rule and RuleCondition (one -> many).
people can add conditions at any time.
Suppose initially he adds two conditions.
He can comeback and add another condition also can update the conditions that are already added.
I am able to save the updated conditions, but not able to insert the extra condition he added.
Below is my code, and it is failing at
rule.RuleConditions.Add(oRuleCon); -- Entity set was modified during enumaration
If I use the approach
oAngieCtxt.RuleConditions.InsertOnSubmit(oRuleCon);
it is not at all inserting the data.
can somebody advise how to handle?
public ActionResult saveMetricRule(Rule rule)
{
bool IsNew = rule.RuleId == 0;
using (NewAngieDataContext oAngieCtxt = new NewAngieDataContext(new CSConfigurationMgr().GetConnectionString(ConnectionStringKey.Angie)))
{
if (IsNew)
oAngieCtxt.Rules.InsertOnSubmit(rule);
else
{
RuleCondition oRuleCon = null;
foreach (RuleCondition childItem in rule.RuleConditions)
{
if (childItem.RuleConditionId == 0)
{
oRuleCon = new RuleCondition();
oRuleCon.Points = childItem.Points;
oRuleCon.ConditionValue = childItem.ConditionValue;
oRuleCon.ToOperatorId = childItem.ToOperatorId;
oRuleCon.Sort = childItem.Sort;
rule.RuleConditions.Add(oRuleCon);
// oAngieCtxt.RuleConditions.InsertOnSubmit(oRuleCon);
}
else
{
oRuleCon =
oAngieCtxt.RuleConditions
.Where(CON => CON.RuleConditionId == childItem.RuleConditionId)
.FirstOrDefault();
oRuleCon.Points = childItem.Points;
oRuleCon.ConditionValue = childItem.ConditionValue;
oRuleCon.ToOperatorId = childItem.ToOperatorId;
oRuleCon.Sort = childItem.Sort;
}
}
oAngieCtxt.Rules.Attach(rule);
oAngieCtxt.Refresh(RefreshMode.KeepCurrentValues, rule);
}
oAngieCtxt.SubmitChanges();
}
return this.Json(new
{
msg = "Successful save.",
ruleId = rule.RuleId
});
}
You can't add an item to a list that you're enumerating over. Since you're looping over the rule.RuleConditions, you can't add to it inside the foreach loop. Instead, you can add to a temporary list and then add all items from that list to the rule.RuleConditions after the foreach.
var newRuleConditions = new List<RuleCondition>();
foreach (RuleCondition childItem in rule.RuleConditions)
{
if (childItem.RuleConditionId == 0)
{
oRuleCon = new RuleCondition();
oRuleCon.Points = childItem.Points;
oRuleCon.ConditionValue = childItem.ConditionValue;
oRuleCon.ToOperatorId = childItem.ToOperatorId;
oRuleCon.Sort = childItem.Sort;
//add to temporary list
newRuleConditions.Add(oRuleCon);
oAngieCtxt.RuleConditions.InsertOnSubmit(oRuleCon);
}
else
{
...
}
}
//add all new rule conditions
rule.RuleConditions.AddRange(newRuleConditions);

Query not updating database

Morning,
I have an issue with some of my code...
Basically i am trying to update or insert into the database. the first if statement if for when adding a new product. the else should then update any existing products.
However when i run it, it is not updating the existing products in the database. It is however setting the items ready to be updated. Any ideas?
Many thanks...
using (aboDataDataContext dc = new aboDataDataContext())
{
foreach (abcProduct p in abcProducts)
{
var match = (from t in dc.abcProducts
where t.sku == p.productcode
select t).FirstOrDefault();
if (match == null)
{
// Watch out here; there is some type conversion required for certain fields!
abcProduct prod = new abcProduct();
prod.sku = p.productcode;
prod.categoryId = dc.Categories.Single(c => c.Name == p.category).Id;
prod.title = p.name;
prod.brand = p.manufacturer;
prod.description = p.description;
prod.abcPrice = p.price;
prod.size = decimal.TryParse(p.size.Replace("cl", ""), out size) == true ? (int?)size : null;
prod.country = p.country;
prod.region = p.region;
prod.vintage = int.TryParse(p.vintage, out vintage) == true ? (int?)vintage : null;
prod.weight = Convert.ToDecimal("1.50");
prod.strength = p.strength;
prod.bottler = p.bottler;
prod.age = int.TryParse(p.age, out age) == true ? (int?)age : null;
prod.caskType = p.casktype;
prod.series = p.series;
prod.flavour = p.flavour;
if (p.freestock <= 0) { prod.stock = 0; } //check to see if stock is 0
else { prod.stock = p.freestock; }
prod.abcUpdated = false;
prod.stockUpdated = false;
prod.priceUpdated = false;
prod.pricePublished = false;
prod.stockPublished = false;
prod.imgPublished = false;
prod.prodPublished = false;
prod.lastUpdated = DateTime.Now;
// Add the new object to the abcProducts table (only in memory here)
dc.abcProducts.InsertOnSubmit(prod);
}
else
{
// update row
match.abcUpdated = true;
//Set if an item has been updated or not.
if (match.stock == p.freestock) { match.stockUpdated = false; }
else { match.stockUpdated = true; }
if (match.abcPrice == p.price) { match.priceUpdated = false; }
else { match.priceUpdated = true;}
match.sku = p.productcode;
match.categoryId = dc.Categories.Single(c => c.Name == p.category).Id;
match.title = p.name;
match.brand = p.manufacturer;
match.description = p.description;
match.stock = p.freestock;
match.abcPrice = p.price;
match.size = decimal.TryParse(p.size.Replace("cl", ""), out size) == true ? (int?)size : null;
match.weight = Convert.ToDecimal("1.50");
match.country = p.country;
match.region = p.region;
match.vintage = int.TryParse(p.vintage, out vintage) == true ? (int?)vintage : null;
match.strength = p.strength;
match.bottler = p.bottler;
match.age = int.TryParse(p.age, out age) == true ? (int?)age : null;
match.caskType = p.casktype;
match.series = p.series;
match.flavour = p.flavour;
if (p.freestock <= 0) { match.stock = 0; } //check to see if stock is 0
else { match.stock = p.freestock; }
match.abcUpdated = true;
match.pricePublished = false;
match.stockPublished = false;
match.imgPublished = false;
match.prodPublished = false;
match.lastUpdated = DateTime.Now;
}
}
// Finally, request Linq to perform the database updates.
dc.SubmitChanges();
}
return null;
}
The context is loosing track of the object when setting match.
At the bottom of the else statement insert
dc.abcProducts.Attach(match);
There is problem in your code, you are iterating through the product table and getting the values in the match variable. In the part of the if statement where match is not null, you are setting the object to the new values, but you are not calling the dc.SubmitChanges();, that object match is not getting stored any where in your code, and in the next iteration in the loop, it is being assigned the new values.
You need to call dc.SubmitChanges(); after update the match values.
foreach (abcProduct p in abcProducts)
{
var match = (from t in dc.abcProducts
where t.sku == p.productcode
select t).FirstOrDefault();
if (match == null)
{
//insertion code commented out
dc.abcProducts.InsertOnSubmit(prod);
}
else
{
///.... setting up all fields.
match.stockPublished = false;
match.imgPublished = false;
match.prodPublished = false;
match.lastUpdated = DateTime.Now;
dc.SubmitChanges(); //Call this otherwise you will
//loose the match values in the next iteration
}
}

Categories