Can Entity Framework add many related entities with single SaveChanges()? - c#

I am writing many (20+) parent child datasets to the database, and EF is requiring me to savechanges between each set, without which it complains about not being able to figure out the primary key. Can the data be flushed to the SQL Server so that EF can get the primary keys back from the identities, with the SaveChanges being sent at the end of writing all of the changes?
foreach (var itemCount in itemCounts)
{
var addItemTracking = new ItemTracking
{
availabilityStatusID = availabilityStatusId,
itemBatchId = itemCount.ItemBatchId,
locationID = locationId,
serialNumber = serialNumber,
trackingQuantityOnHand = itemCount.CycleQuantity
};
_context.ItemTrackings.Add(addItemTracking);
_context.SaveChanges();
var addInventoryTransaction = new InventoryTransaction
{
activityHistoryID = newInventoryTransaction.activityHistoryID,
itemTrackingID = addItemTracking.ItemTrackingID,
personID = newInventoryTransaction.personID,
usageTransactionTypeId = newInventoryTransaction.usageTransactionTypeId,
transactionDate = newInventoryTransaction.transactionDate,
usageQuantity = usageMultiplier * itemCount.CycleQuantity
};
_context.InventoryTransactions.Add(addInventoryTransaction);
_context.SaveChanges();
}
I would like to do my SaveChanges just once at the end of the big loop.

You don`t need to save changes every time if you use objects refernces to newly created objects not IDs:
var addItemTracking = new ItemTracking
{
...
}
_context.ItemTrackings.Add(addItemTracking);
var addInventoryTransaction = new InventoryTransaction
{
itemTracking = addItemTracking,
...
};
_context.InventoryTransactions.Add(addInventoryTransaction);
...
_context.SaveChanges();

Since they're all new items rather than
itemTrackingID = addItemTracking.ItemTrackingID,
you could go with
addItemTracking.InventoryTransaction = addInventoryTransaction;
(or whatever the associated navigation property is) and pull the _context.SaveChanges() out of the loop entirely. Entity Framework is very good at inserting object graphs when everything is new. When saving object graphs containing both new and existing items setting the associated id is always safer.

How about:
var trackingItems = itemCounts
.Select(i => new ItemTracking
{
availabilityStatusID = availabilityStatusId,
itemBatchId = i.ItemBatchId,
locationID = locationId,
serialNumber = serialNumber,
trackingQuantityOnHand = i.CycleQuantity
});
_context.ItemTrackings.AddRange(trackingItems);
_context.SaveChanges();
var inventoryTransactions = trackingItems
.Select(t => new InventoryTransaction
{
activityHistoryID = newInventoryTransaction.activityHistoryID,
itemTrackingID = t.ItemTrackingID,
personID = newInventoryTransaction.personID,
usageTransactionTypeId = newInventoryTransaction.usageTransactionTypeId,
transactionDate = newInventoryTransaction.transactionDate,
usageQuantity = usageMultiplier * t.trackingQuantityOnHand
});
_context.InventoryTransactions.AddRange(inventoryTransactions);
_context.SaveChanges();
However I haven't worked with EF for quite a while and above code is written in notepad so I cannot vouch for it

Related

DbSet.Where() Returning No Records on Query Even When They Exist in Dataset

Okay, so I'm going crazy here. I've used DbSet.Where's 1000 times and for whatever reason it's not working in this particular xunit test. The issue seems to be rooted with my where statement trying to get a list of recipeid's = 1 so I can delete them. Whe I stop th ecode and look at my locals the params are set to 1 where designated, but the where won't pick it up.
I've consolidated the code a bit to make it more readable here, but it still doesn't work as is. What the heck am I missing?
[Fact]
public void DeleteIngredientListWithId_ReturnsProperCount()
{
//Arrange
var dbOptions = new DbContextOptionsBuilder<IngredientDbContext>()
.UseInMemoryDatabase(databaseName: $"IngredientDb{Guid.NewGuid()}")
.Options;
var sieveOptions = Options.Create(new SieveOptions());
var fakeIngredientOne = new Ingredient { RecipeId = 1 };
var fakeIngredientTwo = new Ingredient { RecipeId = 1 };
var fakeIngredientThree = new Ingredient { RecipeId = 2 };
//Act
using (var context = new IngredientDbContext(dbOptions))
{
context.Ingredients.AddRange(fakeIngredientOne, fakeIngredientTwo, fakeIngredientThree);
var service = new IngredientRepository(context, new SieveProcessor(sieveOptions));
var ingredients = context.Ingredients.Where(i => i.RecipeId == 1).ToList();
context.Ingredients.RemoveRange(ingredients);
context.SaveChanges();
//Assert
var ingredientList = context.Ingredients.ToList();
ingredientList.Should().ContainEquivalentOf(fakeIngredientThree);
ingredientList.Should().HaveCount(1);
context.Database.EnsureDeleted();
}
}
It looks like you might not be persisting the records you added to the database before you subsequently try to query (and then remove them). The Where method is looking at the database, which is empty until you SaveChanges()
Until you save the changes, the pending additions are probably waiting for you in context.Ingredients.Local

How to execute a stored procedure join without creating a new asp.net model?

I am creating stored procedures in SQL Server database and have been having issues returning the data when called.
I have managed to get it to work, but it feels as if it is a hack job and that I am doing it incorrectly. Please see the code below and let me know if there is a better way to go about doing this. Thank you for taking the time to help me.
create procedure FetchSumOfEmpSalariesByCity
as
begin
select
sum(e.SAL) as TotalSalary, d.LOC as Location
from
EMPS e
join
DEPTs d on e.DEPTNO = d.DEPTNO
group by
d.LOC
end
public class SumOfEmpsSalaryByCity
{
[Key]
public int TotalSalary { get; set; }
public string Location { get; set; }
}
[HttpGet]
[Route("salary")]
public IHttpActionResult GetEMPsSal()
{
using (var db = new KemmitContext())
{
var sp = db.SumOfEmpsSalaryByCities.SqlQuery("FetchSumOfEmpSalariesByCity");
return Ok(sp.ToList());
}
}
I want to do this the correct way. Is there a way to do this without a model? Or am I going about this the right way?
I break these tasks down like this; should it be done with EF or in the database and if it's in the database, should it be a View or an Sp?
Whenever I'm simply selecting data, I use EF either direct to the table for very simple queries or I create a database View for any joins, etc. This can be done in EF but it's god-awful, in any case, IMO these tasks belong in the database, right tool, right job. If you're using code-first, getting your Views across is a bit involved, let me know if you're doing that.
var model = db.v_ObservationAutoComplete // This can be direct to a table or a view
.Where(oa => oa.Observation.Contains(term))
.OrderBy(oa => oa.Observation)
.Select(oa => new
{
label = oa.Observation
}).Take(10);
When I have to update something in a single table, I use EF
t_Section eSection = new t_Section
{
SectionId = model.SectionId,
Section = model.Section,
SectionTypeId = model.SectionTypeId,
SectionOrdinal = model.SectionOrdinal,
ModifyDate = DateTime.Now,
ModifyUserName = User.Identity.Name,
LastChangeId = newChangeId
};
db.Entry(eSection).State = EntityState.Modified;
db.SaveChanges();
If I have to do a multi-table update, I have a couple of different methodologies; 1) returning a simple bool value for a status code/scalar value, or I have to return a result set after doing whatever update I made.
This returns a List
List<string> elements = new List<string>();
try
{
SqlParameter[] parms = new[]
{
new SqlParameter("mpid", myProtocolsId),
new SqlParameter("elid", elementId)
};
elements = db.Database.SqlQuery<string>("p_MyProtocolsOverviewElementRemove #myProtocolsId = #mpid, #elementId = #elid", parms).ToList();
return Json(elements);
}
catch (Exception e)
{
return Json(null);
}
And if I just need a simple value back, something like this;
SqlParameter[] parms = new[]
{
new SqlParameter("chid", changeId),
new SqlParameter("prid", change.ObjectId),
new SqlParameter("psid", change.ProtocolSectionId),
new SqlParameter("inid", change.SecondaryObjectId),
new SqlParameter("acun", User.Identity.Name)
};
result = db.Database.SqlQuery<int>("p_MyProtocolsContentUpdateInterventionAdd #changeId = #chid, #protocolId = #prid, #protocolSectionId = #psid, #interventionId = #inid, #acceptUserName = #acun", parms).FirstOrDefault();
Hope this helps!

Update Object without Select EF6 MySQL

Is it possible to update objects with Entity Framework, without grabbing them first?
Example: Here, I have a function that provides a Primary Key to locate the objects, pulls them, then updates them. I would like to eliminate having to pull the objects first, and simply run an UPDATE query. Removing the need for the SELECT query being generated.
public async Task<int> UpdateChecks(long? acctId, string payorname, string checkaccountnumber, string checkroutingnumber, string checkaccounttype)
{
using (var max = new Max(_max.ConnectionString))
{
var payments = await
max.payments.Where(
w =>
w.maindatabaseid == acctId && (w.paymentstatus == "PENDING" || w.paymentstatus == "HOLD")).ToListAsync();
payments.AsParallel().ForAll(payment =>
{
payment.payorname = payorname;
payment.checkaccountnumber = checkaccountnumber;
payment.checkroutingnumber = checkroutingnumber;
payment.checkaccounttype = checkaccounttype;
payment.paymentmethod = "CHECK";
payment.paymentstatus = "HOLD";
});
await max.SaveChangesAsync();
return payments.Count;
}
}
You can use the Attach() command to attach an entity you already know exists and then call SaveChanges() will will call the appropriate update method. Here is some sample code from the MSDN article on the topic:
on the subject:
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Entry(existingBlog).State = EntityState.Unchanged;
// Do some more work...
context.SaveChanges();
}
Note that this is general EF logic, not related to any specific database implementation.

C# Linq is removing a value from my entity

So, in a desperate attempt to wrangle EntityFramework into being usable. I am here..
private MyEntity Update(MyEntity orig)
{
//need a fresh copy so we can attach without adding timestamps
//to every table....
MyEntity ent;
using (var db = new DataContext())
{
ent = db.MyEntities.Single(x => x.Id == orig.Id);
}
//fill a new one with the values of the one we want to save
var cpy = new Payment()
{
//pk
ID = orig.ID,
//foerign key
MethodId = orig.MethodId,
//other fields
Information = orig.Information,
Amount = orig.Amount,
Approved = orig.Approved,
AwardedPoints = orig.AwardedPoints,
DateReceived = orig.DateReceived
};
//attach it
_ctx.MyEntities.Attach(cpy, ent);
//submit the changes
_ctx.SubmitChanges();
}
_ctx is an instance variable for the repository this method is in.
The problem is that when I call SubmitChanges, the value of MethodId in the newly attached copy is sent to the server as 0, when it is in fact not zero if I print it out after the attach but before the submit. I am almost certain that is related to the fact that the field is a foreign key, but I still do not see why Linq would arbitrarily set it to zero when it has a valid value that meets the requirements of the constraint on the foreign key.
What am I missing here?
You should probably set Method = orig.Method, but I can't see your dbml, of course.
I think you need to attach the foreign key reference
var cpy = new Payment()
{
//pk
ID = orig.ID,
//other fields
Information = orig.Information,
Amount = orig.Amount,
Approved = orig.Approved,
AwardedPoints = orig.AwardedPoints,
DateReceived = orig.DateReceived
};
//create stub entity for the Method and Add it.
var method = new Method{MethodId=orig.MethodId)
_ctx.AttachTo("Methods", method);
cpy.Methods.Add(method);
//attach it
_ctx.MyEntities.Attach(cpy, o);
//submit the changes
_ctx.SubmitChanges();

How to get yet-to-be-created ID in Linq/Entity framework

I have two table like this:
**Complaint**
-Id
-CreatedBy
-CreatedDate
....
**Solution**
-Id
-ComplaintId
Sometimes, a complaint has an instant solution, which means, when it is created, a solution is also created. The Database is Oracle, and to insert new record into database, I set the StoredGeneratePattern to Identity and use trigger to insert a sequence's value.
here my code:
using (var context = new Entities())
{
var complaint = new Complaint
{
Title = TitleTextBox.Text.Trim(),
CreatedBy = CurrentUser.UserID,
Description = DescriptionTextBox.Text.Trim(),
ServiceId = Convert.ToDecimal(ddlService2.Value),
Contact = ContactTextBox.Text.Trim(),
CreatedDate = DateTime.Now,
Customer = txtUserName.Text.Trim(),
ResellerId = CurrentUser.ResellerID,
Status = ComplaintStatus.GetStatusCode("New complaint")
};
if (CompletedCheckBox.Checked)
{
complaint.Status = ComplaintStatus.GetStatusCode("Completed");
var solution = new Solution
{
CreatedBy = CurrentUser.UserID,
CreatedDate = DateTime.Now,
SolutionDesc = DescriptionTextBox.Text,
ComplaintId = complaint.Id
};
context.Solutions.AddObject(solution);
}
context.Complaints.AddObject(complaint);
if(context.SaveChanges() > 0)
{
ResetFrom();
return true;
}
}
the problem is, I can't get the id of newly created complaint to set the field in the solution. How can I do that?
Thank you.
Could you not perform the first operation call SaveChanges() and then query your complaint object which should now have a complaintID.
Assuming you are using a trigger/sequence with Oracle, you will need to do a get after you save changes to get an object with the Id populated. If you are not using a trigger, you can set the Id manually on the new object by getting the next value from the sequence.
If you add the complaint and SaveChanges() before you create the solution the complaint object will have the Identity value, then after creating the solution add it to the context and call SaveChanges() a second time.
context.Complaints.AddObject(complaint);
if (CompletedCheckBox.Checked)
{
complaint.Status = ComplaintStatus.GetStatusCode("Completed");
context.SaveChanges();
var solution = new Solution
{
CreatedBy = CurrentUser.UserID,
CreatedDate = DateTime.Now,
SolutionDesc = DescriptionTextBox.Text,
ComplaintId = complaint.Id
};
context.Solutions.AddObject(solution);
}
if(context.SaveChanges() > 0)
{
ResetFrom();
return true;
}
Also if you were to add a Foreign Key Relationship Between the Solution and the Complaint, you would no set the ComplaintId, you would just set solution.Complaint = complaint and the Ids would be set correctly during the save.
The answer is actually easy. In this case I do not believe you need the ID at all (at least not just to add this relationship), but in case you do, do this:
Make sure you have the ID on the Complaint entity to refresh on Insert (We use DevArt, but I forget the exact setting name here, but if you select the ID on the entity you should see an UpdateMode or something like that that needs to be set to UpdateOnInsert i think), then
To just insert this relationship do this:
using (var context = new MyContext())
{
var complaint = new Complaint {...};
context.Complaints.AddObject(complaint);
var solution = new Solution {..., Complaint = complaint};
context.Solutions.AddObject(solution);
context.SaveChanges();
}
You will not want to do SaveChanges twice as that requires a separate transactionscope. This way you don't need it.
You can add the Complaint to the Solutions "Complaint" navigation property.
So create your solution object like you are doing then do the following:
Soltion.Complaint = newCreatedComplaintObject;

Categories