Re-Select Changed But Not Saved Item in Entity Framework - c#

TestEntities context = new TestEntities();
var item = context.TestTables.Single(s => s.ID == 1);//item.Name is "Giorgi"
item.Name = "Hello";
var item1 = context.TestTables.Single(s => s.ID == 1);
Console.WriteLine(item1.Name);
context.SaveChanges();
What do you expect to be written? Hello is written! Why?
TestEntities context = new TestEntities();
var item = context.TestTables.Single(s => s.ID == 1);//item.Name is "Giorgi"
item.Name = "Hello";
var item1 = context.TestTables.Single(s => s.ID == 1);
context.SaveChanges();
Console.WriteLine(item1.Name);
What do you expect to be written? Hello is written! Why?
* there are two different questions*

Your changes are registered in the context, although not saved to the database until you call SaveChanges. If you need the original value, you could either open a new context, reload the entity or inspect the change tracker for changes.
Added tests:
[Fact]
public void TestUsingNewContext()
{
using (var context = new TestEntities())
{
var item = context.TestTables.Single(s => s.ID == 1);
item.Name = "Hello";
using (var newContext = new TestEntities())
{
var item1 = newContext.TestTables.Single(s => s.ID == 1);
Assert.Equal("Giorgi", item1.Name);
}
}
}
[Fact]
public void TestUsingReload()
{
using (var context = new TestEntities())
{
var item = context.TestTables.Single(s => s.ID == 1);
item.Name = "Hello";
context.Entry(item).Reload();
var item1 = context.TestTables.Single(s => s.ID == 1);
Assert.Equal("Giorgi", item1.Name);
}
}
[Fact]
public void TestUsingChangeTracker()
{
using (var context = new TestEntities())
{
var item = context.TestTables.Single(s => s.ID == 1);
item.Name = "Hello";
foreach (var entry in context.ChangeTracker.Entries<TestTable>().Where(e => e.State == EntityState.Modified))
{
entry.CurrentValues.SetValues(entry.OriginalValues);
}
var item1 = context.TestTables.Single(s => s.ID == 1);
Assert.Equal("Giorgi", item1.Name);
}
}

The "context" lives in memory, so when you change stuff in context they are not changed in the database, but they are changed in the "context" (memory), only when you call context.SaveChanges() you actually persisting your updats/changes to the database.
Why would you need SaveChanges() if not to actually save the changes..

In both cases item and item1 linked with same entity in context. EntityFramework store entity in context for you. I supose when you select it again it returns cached copy. When you change some entity in context anyone who work with this context see this changes. Changes goes to database only after comit.
using(TestEntities context = new TestEntities())
{
var item = context.TestTables.Single(s => s.ID == 1);//item.Name is "Giorgi"
item.Name = "Hello";
using(TestEntities context = new TestEntities())
{
var item1 = context.TestTables.Single(s => s.ID == 1);
Console.WriteLine(item1.Name); // you will get old value here
}
}
Try to read about it in msdn. Bacause to much stones underwater here. For example: what will be if some one change your entity in db and you try to commit your changes after?
http://msdn.microsoft.com/en-us/data/ee712907
http://msdn.microsoft.com/en-us/data/jj592904.aspx

Related

EF .Net Core Enumerable.Concat not working

Below is my code
var dbClaimLink = this.Context.Set<ClaimLink>();
var claims = await DbSet
.Include(claim => claim.Parent)
.Include(link => link.ParentLinks)
.ToListAsync();
var newClaimLink = await dbClaimLink.ToListAsync();
var processedClaims = claims.Select(x =>
{
var claimLinks = x.ParentLinks;
if (!claimLinks.Any())
{
return x;
}
var hiddenParents = claimLinks.Select(p => claims.Find(t => t.Id == p.ClaimLinkId));
x.HiddenParents = hiddenParents;
return x;
});
foreach (var objClaim in processedClaims)
{
if (objClaim.Children == null)
objClaim.Children = new List<Claim>();
var lst = newClaimLink.Where(k=> k.ClaimLinkId == objClaim.Id).ToList();
if (lst.Any())
{
foreach (var item in lst)
{
IEnumerable<Claim> newChildren = claims.Where(p => p.Id == item.ClaimId);
objClaim.Children.Concat(newChildren);
}
}
}
it always return old children set without concatenate with new children. I want to those old and new children set concatenate in side of foreach loop
the Concat method returns a new collection with both values and does not alter the original.
Concat will return new object - result of concatination, so you need to save it somewhere: var result = objClaim.Children.Concat(newChildren);
Where is lazy operation, it does not execute in place, only after materialization (ToArray, or foreach call): claims.Where(p => p.Id == item.ClaimId).ToArray()

Entity Framework M:1 relationship resulting in primay key duplication

I'm somewhat new to EF 6.0 so I'm pretty sure I'm doing something wrong here.
there are two questions related to the problem
what am I doing wrong here
what's the best practice to achieve this
I'm using a code first model, and used the edmx designer to design the model and relationships, the system needs to pull information periodically from a webservice and save it to a local database (SQL Lite) in a desktop application
so I get an order list from the API, when I populate and try to save Ticket, I get a duplicate key exception when trying to insert TicketSeatType -
how do I insert the ticket to dbContext, so that It doesn't try and re-insert insert TicketSeatType and TicketPriceType, I have tried setting the child object states to unchanged but it seems to be inserting
secondly, what would be the best practice to achieve this using EF ? it just looks very inefficient loading each object into memory and comparing if it exists or not
since I need to update the listing periodically, I have to check against each object in the database if it exists, then update, else insert
code:
//read session from db
if (logger.IsDebugEnabled) logger.Debug("reading session from db");
dbSession = dbContext.SessionSet.Where(x => x.Id == sessionId).FirstOrDefault();
//populate orders
List<Order> orders = (from e in ordersList
select new Order {
Id = e.OrderId,
CallCentreNotes = e.CallCentreNotes,
DoorEntryCount = e.DoorEntryCount,
DoorEntryTime = e.DoorEntryTime,
OrderDate = e.OrderDate,
SpecialInstructions = e.SpecialInstructions,
TotalValue = e.TotalValue,
//populate parent refernece
Session = dbSession
}).ToList();
//check and save order
foreach (var o in orders) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.Id).FirstOrDefault();
if (dbOrder != null) {
dbContext.Entry(dbOrder).CurrentValues.SetValues(o);
dbContext.Entry(dbOrder).State = EntityState.Modified;
}
else {
dbContext.OrderSet.Add(o);
dbContext.Entry(o.Session).State = EntityState.Unchanged;
}
}
dbContext.SaveChanges();
//check and add ticket seat type
foreach (var o in ordersList) {
foreach (var t in o.Tickets) {
var ticketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
};
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == ticketSeatType.Id).FirstOrDefault();
if (dbTicketSeatType != null) {
dbContext.Entry(dbTicketSeatType).CurrentValues.SetValues(ticketSeatType);
dbContext.Entry(dbTicketSeatType).State = EntityState.Modified;
}
else {
if (!dbContext.ChangeTracker.Entries<TicketSeatType>().Any(x => x.Entity.Id == ticketSeatType.Id)) {
dbContext.TicketSeatTypeSet.Add(ticketSeatType);
}
}
}
}
dbContext.SaveChanges();
//check and add ticket price type
foreach (var o in ordersList) {
foreach (var t in o.Tickets) {
var ticketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
SeatCount = t.TicketPriceType.SeatCount,
Description = t.TicketPriceType.Description
};
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == ticketPriceType.Id).FirstOrDefault();
if (dbTicketPriceType != null) {
dbContext.Entry(dbTicketPriceType).CurrentValues.SetValues(ticketPriceType);
dbContext.Entry(dbTicketPriceType).State = EntityState.Modified;
}
else {
if (!dbContext.ChangeTracker.Entries<TicketPriceType>().Any(x => x.Entity.Id == ticketPriceType.Id)) {
dbContext.TicketPriceTypeSet.Add(ticketPriceType);
}
}
}
}
dbContext.SaveChanges();
//check and add tickets
foreach (var o in ordersList) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
foreach (var t in o.Tickets) {
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
Description = t.TicketPriceType.Description,
SeatCount = t.TicketPriceType.SeatCount,
},
TicketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
},
Order = dbOrder
};
//check from db
dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();
if (dbTicket != null) {
dbContext.Entry(dbTicket).CurrentValues.SetValues(t);
dbContext.Entry(dbTicket).State = EntityState.Modified;
dbContext.Entry(dbTicket.Order).State = EntityState.Unchanged;
dbContext.Entry(dbTicketSeatType).State = EntityState.Unchanged;
dbContext.Entry(dbTicketPriceType).State = EntityState.Unchanged;
}
else {
dbContext.TicketSet.Add(ticket);
dbContext.Entry(ticket.Order).State = EntityState.Unchanged;
dbContext.Entry(ticket.TicketSeatType).State = EntityState.Unchanged;
dbContext.Entry(ticket.TicketPriceType).State = EntityState.Unchanged;
}
}
}
dbContext.SaveChanges();
UPDATE:
Found the answer, it has to do with how EF tracks references to objects, in the above code, I was creating new entity types from the list for TicketPriceType and TicketSeatType:
foreach (var o in ordersList) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
foreach (var t in o.Tickets) {
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
Description = t.TicketPriceType.Description,
SeatCount = t.TicketPriceType.SeatCount,
},
TicketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
},
Order = dbOrder
};
....
in this case the EF wouldn't know which objects they were and try to insert them.
the solution is to read the entities from database and allocate those, so it's referencing the same entities and doesn't add new ones
foreach (var t in o.Tickets) {
//check from db
dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = dbTicketPriceType,
TicketSeatType = dbTicketSeatType,
Order = dbOrder
};
...}
Don't you think that you are trying to write very similar codes for defining the state of each entity?
We can handle all of these operations with a single command.
You can easily achieve this with the newly released EntityGraphOperations for Entity Framework Code First. I am the author of this product. And I have published it in the github, code-project (includes a step-by-step demonstration and a sample project is ready for downloading) and nuget. With the help of InsertOrUpdateGraph method, it will automatically set your entities as Added or Modified. And with the help of DeleteMissingEntities method, you can delete those entities which exists in the database, but not in the current collection.
// This will set the state of the main entity and all of it's navigational
// properties as `Added` or `Modified`.
context.InsertOrUpdateGraph(ticket);
By the way, I feel the need to mention that this wouldn't be the most efficient way of course. The general idea is to get the desired entity from the database and define the state of the entity. It would be as efficient as possible.

Entity Framework condition in select

I have the following database code:
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
if (withchildren) {
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email,
Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
});
} else {
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email
});
}
return res.ToList()
}
}
I would like to shrink the code and write it like this:
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
res = model.UserSet
.Where(u => u.name != "")
.Select(u => {
dynamic item = new {
Name = u.name,
Email = u.email
};
if(withchildren) {
item.Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
});
}
return item;
});
return res.ToList();
}
}
But Visual Studio complains, that it cannot convert the lambda expression into an expression tree.
My question is, is there a way to accomplish that with the Entity Framework and Linq? I really wouldn't want to use ADO.net directly.
Maybe there is even a better version to shrink it, than the code that I imagine.
Here is a related question with Linq-To-Objects.
EDIT
Before someone asks, I use dynamic in the example code to make it a bit easier and faster.
EDIT 2
My goal with this approach is, to only query the fields I need to improve performance. Check http://www.progware.org/Blog/post/Slow-Performance-Is-it-the-Entity-Framework-or-you.aspx.
At the moment we use something like
static IEnumerable<dynamic> GetData(bool withchildren) {
using (var model = new testEntities()) {
var res = default(IQueryable<dynamic>);
res = model.UserSet
.Where(u => u.name != "")
.ToList();
return res;
}
}
And the performance is, according to Glimpse, horrible.
EDIT 3
Short side note, I made up some quick and dirty code. That is, why the foreach at the end was not needed. The actual code is not available at the moment.
Is there any reason you couldn't use:
res = model.UserSet
.Where(u => u.name != "")
.Select(u => new {
Name = u.name,
Email = u.email,
Groups = withchildren
? u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
: null;
})
};
or perhaps:
res = model.UserSet
.Where(u => u.name != "")
.ToList() // ToList() here to enumerate the items
.Select(u => {
dynamic item = new {
Name = u.name,
Email = u.email
};
if(withchildren) {
item.Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
});
}
return item;
});
One approach that would allow you to eliminate some code would be:
var res = model.UserSet.Where(u => u.name != "");
if (withchildren) {
res = res.Select(u => new {
Name = u.name,
Email = u.email,
Groups = u.GroupSet.Select(g => new {
Name = g.name,
Id = g.Id
})
});
} else {
res = res.Select(u => new {
Name = u.name,
Email = u.email
});
}
One of the most requested by community feature is support of multi-line expressions in EF,
however so far, you can use only conditional operator "?:" as well as wrap result in
one common type, so both your results will have to have "Groups" field.
Also there are an extensions to linq-providers such as https://www.nuget.org/packages/LinqKit/ ,
however they are based on own conventions so any developer should study it in depth before
taking advance in applying and supporting code written upon that extensions.

Delete Many to Many relationship using Entity Framework

I've three tables Student (studID, fullName, gender...), Enroll (studID, courseID, date) and Course (courseID,courseName, ...). I used the code below to delete all records from Enroll table with studID 001 where there are about three courses the student signed for. However, it only deletes one record.
using(var context = new DBEntities())
{
var _stud = (from s in context.Students where s.studID == "001" select s).FirstOrDefault();
var _course = _stud.Courses.FirstOrDefault();
_course.Students.Remove(_stud);
context.SaveChanges();
}
What do I miss here?
Thank you guys for assisting. Here is how I solved it:
using (var context = new DBEntities())
{
var student = (from s in context.Students where s.studID == "001" select s).FirstOrDefault<Student>();
foreach (Course c in student.Courses.ToList())
{
student.Courses.Remove(c);
}
context.SaveChanges();
}
I used the code below to delete all records from Enroll table
Are you deleting enrolls or students?
Student student = context.Student.FirstOrDefault(s => s.studID == "001");
if (student!=null)
{
student.Enrolls.Load();
student.Enrolls.ToList().ForEach(e => context.Enroll.DeleteObject(e));
}
Have you try this code :
var student = context.Students.Where(p => p.studID == "001").ToList();
foreach (var item in student)
{
if (student != null)
{
var course = student.Courses.ToList();
if (course != null)
{
foreach (var item2 in course)
{
course.Students.Remove(item2);
context.SaveChanges();
}
}
}
}
For others looking to this answer you could also do it using include.
using(var context = new DBEntities())
{
// Get student by id
var student = context.Students.Include(s => s.Courses).Where(s => s.studID == "001").FirstOrDefault();
if(student.Courses != null)
{
// Retrieve list of courses for that student
var coursesToRemove = stud.Courses.ToList();
// Remove courses
foreach (var course in coursesToRemove)
{
student.Courses.Remove(course);
}
// Save changes
context.SaveChanges();
}
}

db.SaveChanges() Exploding with Transaction Exception

The following method is causing the FIRST db.SaveChanges() to throw an exception:
(New transaction is not allowed because there are other threads running in the session.)
protected void btnInsertDependencies_Click(object sender, EventArgs e)
{
using (icmsEntities db = new icmsEntities())
{
var divisions = db.Divisions;
foreach (var division in divisions)
{
var dlc_sectors = db.Sectors.Where(s => s.Website.division_id == division.division_id).DistinctBy(s => s.name).ToList();
foreach (var sector in dlc_sectors)
{
dlc_Sector dlc_sector = new dlc_Sector
{
name = sector.name,
division_id = sector.Website.division_id
};
db.dlc_Sectors.AddObject(dlc_sector);
var dlc_products = db.Products.Where(p => p.Sectors
.Any(s => s.Website.division_id == dlc_sector.division_id && s.name == dlc_sector.name))
.DistinctBy(p => p.name).ToList();
foreach (var product in dlc_products.ToList())
{
var dlc_product = db.dlc_Products.SingleOrDefault(p => p.name == product.name);
if (dlc_product == null)
{
dlc_product = new dlc_Product { name = product.name };
db.dlc_Products.AddObject(dlc_product);
}
dlc_product.dlc_Sectors.Add(dlc_sector);
db.SaveChanges();
}
}
}
db.SaveChanges();
}
}
The first db.SaveChanges() is to make sure that a new product having the same name does not get inserted again.
How can I resolve this? Thanks in advance.
Worked for me:
var Local = db.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added)
.Where(es => es.Entity is dlc_Product).Select(es => es.Entity as dlc_Product);
var dlc_product = db.dlc_Products.SingleOrDefault(p => p.name == product.name)
?? Local.SingleOrDefault(p => p.name == product.name);
Thanks.
Not sure why this occurs, but the first SaveChanges() would not be necessary if you also checked the Local collection of db.dlc_Products:
foreach (var product in dlc_products.ToList())
{
var dlc_product = db.dlc_Products.SingleOrDefault(p => p.name == product.name)
?? db.dlc_Products.Local.SingleOrDefault(p => p.name == product.name);
...
}
New objects are added to the Local collection.
Note that db.dlc_Products.Join(db.dlc_Products.Local) looks more efficient, but it does not compile, and the other way around it queries the whole db.dlc_Products for each call.

Categories

Resources