Which is the good way to update object in EF6 - entity-framework

I have searched and find 2 way to update object in EF
var attachedEntity = _context.EntityClasses.Local.First(t => t.Id == entity.Id);
//We have it in the context, need to update.
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
////If it's not found locally, we can attach it by setting state to modified.
////This would result in a SQL update statement for all fields
////when SaveChanges is called.
var entry = _context.Entry(entity);
entry.State = EntityState.Modified;
}
_context.SaveChanges();
And other way is seem more easy
var entity = _context.EntityClasses.FirstOrDefault(t => t.Id == entity.Id);
_context.Entry(entity ).EntityState.Modified
_context.SaveChanges();
What is best way to update object?
NOTE: the performence is importance with me

_context.EntityClasses.Local.First(t => t.Id == entity.Id)
=> means that you want to double check the entity on local (the latest loading from DB) and it is not send to DB to find the record so the performance is faster.
_context.EntityClasses.FirstOrDefault(t => t.Id == entity.Id): This command is look up the entity in DB. That means EF creates the query and look up in DB.
The below link is the difference of between Entity.Local.Find & Entity.Find http://msdn.microsoft.com/en-us/data/jj592872.aspx
Hope it helps!

Related

EF Core entity refresh

I would like if there is a way to trigger an object hierachy's lazy load after it has been added in the cache following an insert.
If I insert an object only providing it the foreign keys ids and try to request it right after, none of these properties / collections are retrieved.
Example:
_dbContext.Set<MYTYPE>().Add(myObject);
await _dbContext.SaveChangesAsync(ct);
_dbContext.Entry(myObject).Collection(c => c.SomeCollection).Load();
_dbContext.Entry(myObject).Reference(c => c.Property1).Load();
_dbContext.Entry(myObject.Property1).Reference(c => c.NestedProperty).Load();
_dbContext.Entry(myObject).Reference(c => c.Property2).Load();
_dbContext.Entry(myObject).Collection(c => c.SomeCollection2).Load();
_dbContext.Entry(myObject).Reference(c => c.Property3).Load();
_dbContext.Entry(myObject).Reference(c => c.Property4).Load();
_dbContext.Entry(myObject.Property4).Reference(c => c.SomeOtherNestedProperty).Load();
_dbContext.Entry(myObject).Reference(c => c.Property5).Load();
_dbContext.Entry(myObject).Reference(c => c.Property6).Load();
return _dbContext.Set<MYTYPE>().Where(x => x.Id == myObject.Id);
Unless I do all the Load() (having to nest into several layers sometimes ...), I can't have any of the sub properties properly lazy loaded.
Note: doing
_dbContext.Entry(myObject).Reload()
or
_dbContext.Entry(myObject).State = EntityState.Detached;
doesn't work.
Any idea? Because I have several cases like this, with HEAVY object nesting, and it feels really bad to have to do all the loads manually.
Thanks.
The thing is, if you try to add some line using only the integer as a foreign key like below :
db.tablename.Add(new tablename{
name = "hello",
FK_IdofanotherTable = 5
});
db.SaveChanges();
Then entity does not care about linking the object immediately, you won't be able to access the linked object as a property.
This is how it's actually done in modern Entity Framework :
var TheActualCompleteObject = db.AnotherTable.First(t => t.id == 5);
db.tablename.Add(new tablename{
name = "hello",
AnotherTable = TheActualCompleteObject
});
db.SaveChanges();
So... basically, you will have loaded the object anyway, it's not gonna make anything faster, but it will be more consistant. You will be able to navigate through foreign keys properties.
If this is not possible, then you will have to query back AFTER SaveChanges to retrieve your object AND have access to foreign properties (eagerly loading them, of course, or else it's the same disaster)
Exemple :
_dbContext.Configuration.LazyLoadingEnabled = false;
_dbContext.Set<MYTYPE>().Add(myObject);
await _dbContext.SaveChangesAsync(ct);
var v = _dbContext.First(t => t.id == myObject.id);
var fk_prop = v.Property1;
I also found no easy way around this.
The following might make it simpler...
public static void Reset(this DbContext context)
{
var entries = context.ChangeTracker
.Entries()
.Where(e => e.State != EntityState.Unchanged)
.ToArray();
foreach (var entry in entries)
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
entry.Reload();
break;
}
}
}
Hope this helps!
JS

Entity Framework 6: Disable Lazy Loading and specifically load included tables

Our current system is using Lazyloading by default (it is something I am going to be disabling but it can't be done right now)
For this basic query I want to return two tables, CustomerNote and Note.
This is my query
using (var newContext = new Entities(true))
{
newContext.Configuration.LazyLoadingEnabled = false;
var result = from customerNotes in newContext.CustomerNotes.Include(d=>d.Note)
join note in newContext.Notes
on customerNotes.NoteId equals note.Id
where customerNotes.CustomerId == customerId
select customerNotes;
return result.ToList();
}
My result however only contains the data in the CustomerNote table
The linked entities Customer and Note are both null, what am I doing wrong here?
I got it working with the following which is much simpler than what I've found elsewhere
Context.Configuration.LazyLoadingEnabled = false;
var result = Context.CustomerNotes.Where<CustomerNote>(d => d.CustomerId == customerId)
.Include(d=>d.Note)
.Include(d=>d.Note.User);
return result.ToList();
This returns my CustomerNote table, related Notes and related Users from the Notes.
That is callled eager loading you want to achieve.
var customerNotes = newContext.CustomerNotes.Include(t=> t.Node).ToList();
This should work, i don't really understand the keyword syntax.
If the code above doesn't work try this:
var customerNotes = newContext.CustomerNotes.Include(t=> t.Node).Select(t=> new {
Node = t.Node,
Item = t
}).ToList();

Run Multiple SaveChanges in transaction in Entity Framework

The following code is separated by TWO .SaveChanges() calls because I have to run some code in fixed order.
First I have to delete existing stuff then add the newly created data.
How can I now assure that both .SaveChanges run in one transaction? Should I wrap a TransactionScope around the SaveChanges?
Are my both SaveChanges() correct at all?
var periodsToDelete = _context.Periods.Where(p => p.SchoolyearId == response.Schoolyear.Id &&
p.LessonDate >= response.DeletionStartDate &&
p.LessonDate <= response.DeletionEndDate).AsNoTracking();
var schoolclassCodesToDelete = _context.SchoolclassCodes.Where(s => s.SchoolyearId == schoolyear.Id).AsNoTracking();
var timetablesToDelete = _context.TimeTables.Where(t => t.SchoolyearId == schoolyear.Id).AsNoTracking();
_context.Periods.RemoveRange(periodsToDelete);
_context.SchoolclassCodes.RemoveRange(schoolclassCodesToDelete);
_context.TimeTables.RemoveRange(timetablesToDelete);
_context.SaveChanges();
_context.Entry(schoolyear).State = EntityState.Modified;
_context.SchoolclassCodes.AddRange(existingAndNewSchoolclassCodes);
_context.TimeTables.AddRange(timetablesA.Concat(timetablesAB));
_context.Periods.AddRange(periods);
_context.SaveChanges();

Entity Framework dbset not finding added entity

I am having an issue understanding why when adding a new entity to a DbSet of ObjectContext, that entity is not found will looking it up again.
using (var db = new SmartrailDB())
{
var cart01 = db.Carts.SingleOrDefault(x => x.Number == 0);
if (cart01 == null)
{
cart01 = new Cart { Number = 0 };
db.Carts.Add(cart01);
}
var cart02 = db.Carts.SingleOrDefault(x => x.Number == 0); // Should find the cart I just added - right?
Assert.IsNotNull(cart02); // Fails because cart02 does not exist in the db.Carts collection
}
Is anyone able to tell me what I am doing wrong here?
Also late on a Friday here so brain half asleep now.
You have to update your context before you try to access the entity. Just do:
db.SaveChanges(); right after db.Cart.Add(cart01);

Entity Framework - Many to Many Subquery

I asked a question about this previously but my database structure has changed, and while it made other things simpler, now this part is more complicated. Here is the previous question.
At the time, my EF Context had a UsersProjects object because there were other properties. Now that I've simplified that table, it is just the keys, so all my EF context knows about is Users and Projects and the M2M relationship between them. There is no more UsersProjects as far as EF knows.
So my goal is to say "show me all the users who are working on projects with me."
in SQL, this would go something like:
SELECT * FROM Users INNER JOIN UsersProjects ON Users.ID=UsersProjects.UserID
WHERE ProjectID IN (SELECT ProjectID FROM UsersProjects WHERE UserID=#UserID)
and I started in EF with something like this:
var myProjects =
(from p in edmx.Projects
where p.Users.Contains(edmx.Users.FirstOrDefault(u => u.Email == UserEmail))
orderby p.Name
select p).ToList();
var associatedUsers =
(from u in edmx.Users
where myProjects.Contains(?????????)
//where myProjects.Any(????????)
select u);
The trick is finding what to put in the ????????. Anyone help here?
var me = context
.Users
.First(user => user.Email = "me#example.com");
// Note that there is no call to ToList() or AsEnumerable().
var myProjects = context
.Projects
.Where(project => project.Users.Contains(me));
var associatedUsers = context
.Users
.Where(user => myProjects.Any(project => user.Project.Contains(project)));
But there are several other possible solutions. For example
var associatedUsers = myProjects
.SelectMany(project => project.Users)
.Distinct();
which I would prefer.
Further note that it is much easier to obtain myProjects using a navigation property instead of using Contains().
var myProjects = me.Projects;
Daniel, I tried what you had and ran into some issues. Can you explain what these errors mean?
I tried:
// Doesn't work.
using (var edmx = new MayflyEntities())
{
var me = edmx.Users.First(user => user.Email == UserEmail);
var myProjects = edmx.Projects.Where(project => project.Users.Contains(me));
var associatedUsers = myProjects.SelectMany(project => project.Users).Distinct();
}
but got the two following exceptions:
Unable to create a constant value of type 'DomainModel.User'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
and
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
So, I moved some things around and this works fine, but now I'm curious as to why? In SQL Profiler, it all executes in one query, so why does it show that the context has been disposed? Also, why can it not use the me object instead of the lambda?
// Works fine
var edmx = new MayflyEntities();
var myProjects = edmx.Projects.Where(project => project.Users.Contains(edmx.Users.First(user => user.Email == UserEmail)));
var associatedUsers = myProjects.SelectMany(project => project.Users).Distinct();