I'm kinda confused about recognizing a disconnected scenario and a connected scenario, I've searched the internet but I couldn't find any real answer to my questions, I'm kinda confused about entities tracking system, connected and disconnected scenarios, when should I use the Attach method and also in differences between using the Entry(entity).State = EntityState.Deleted and Remove(entity) method, and while I was searching about the last one, most of the time, they were thought identical, but it didn't match with the test that I did and what I expected
I just made a simple console app to test the differences, and how it works is that I make a person completely outside of the context instantiation scope and then pass it to the AddPerson method, because I think this makes a disconnected scenario, right? because the Remove method will complain about why I haven't attached the entity first, so I think that tells us that we're in a disconnected scenario, I'm not sure tho
This is the app:
class Program
{
static void Main(string[] args)
{
Person person = new Person()
{
PersonID = 1,
Name = "John",
Family = "Doe"
};
using (var context = new MyContext())
{
// Why this one requires attaching but the code below doesn't
context.Person.Attach(person);
context.Person.Remove(person);
context.SaveChanges();
// This method of deleting works fine without the entity being attached
context.Entry(person).State = EntityState.Deleted;
context.SaveChanges();
var people = context.Person.ToList();
foreach (var p in people)
{
Console.WriteLine($"PersonID: {p.PersonID} | Name: {p.Name} | Family: {p.Family}");
}
}
Console.ReadKey();
}
}
so for the Remove method, I have to Attach the entity first, otherwise, it will throw an exception, BUT when I use the Entry(person).state = EntityState.Deleted without attaching it, it works fine, and deletes the person, now why is that, isn't this a big difference? why is it not said anywhere, I've read some websites and some other similar questions on Stackoverflow too, but this wasn't said anywhere, and for the most part, these two were presumed to be the same, and do the same thing, yes they both delete the entity, but how can we describe what happened in this test, isn't this a difference between these two?
I have two questions but I think they're related to each other, so I'm just going to ask both of them here:
When does exactly a disconnected scenario happen, and how can I recognize it, does it depend on the scope of the context instantiation, or on retrieving the entity directly from the context and then modifying it (with no need to attach it), or using an entity from outside of the context (like passing it from another scope to our context as a parameter, as I did in my test)?
Why does the Remove method requires attaching but the EntityState.Deleted doesn't, but they're presumed identical? why should I even bother to attach the entity first, while setting the state to deleted works without needing to attach, so When to use each of them?
Basically, The way I assume that how all these work (with my current understanding of Entity Framework which is probably wrong) is that when you're in a disconnected scenario, you have to attach your entity first, but then setting the state to EntityState.Deleted doesn't need attaching, so then why does the Remove method exists at all, we could use the other way of deleting all the time.
EDIT:
Based on the second code block in the accepted answer, I wrote this test, to figure out how it's working, you said that the otherPersonReference is equal to having a Attach(Person) but when I first attach the person and try to use EntityState.Deleted It works then too, and it'll delete it, but you said that it would fail, I'm a little confused :s
class Program
{
static void Main(string[] args)
{
Person person = new Person()
{
PersonID = 3,
Name = "John",
Family = "Doe"
};
using (var context = new MyContext())
{
//var pr = context.Person.Single(p => p.PersonID == 3);
context.Person.Attach(person);
context.Entry(person).State = EntityState.Deleted;
context.SaveChanges();
}
Console.ReadKey();
}
}
if I uncomment the pr variable line and then comment the context.Person.Attach(person) then setting the EntityState to Deleted would fail and it'll throw an exception as expected
Setting context.Entry(person).State tells EF to start tracking the "person" instance if it isn't already tracking it. You would get an error if the DbContext was already tracking an instance for the same record.
For example, you can try the following:
var person = new Person { Id = 100 }; // assume an existing record with ID = 100;
using (var context = new AppDbContext())
{
context.Entry(person).State = EntityState.Deleted;
context.SaveChanges();
}
This works as you expect... However, if you were to have code that did this:
var person = new Person { Id = 100 }; // assume an existing record with ID = 100;
using (var context = new AppDbContext())
{
var otherPersonReference = context.Persons.Single(x => x.Id == 100);
context.Entry(person).State = EntityState.Deleted;
context.SaveChanges();
}
Your attempt to use context.Entry(person).State = EntityState.Deleted; would fail because the context is now already tracking an entity with that ID. It's the same behaviour as if you were to try and call Attach(person).
When dealing with short-lived DbContexts (such as when using using() blocks) and single entity operations, it can be reasonably safe to work with detached entity references, but this will get a lot more "iffy" once you start dealing with multiple possible entity references (I.e. working with lists or objects sharing references etc.) and/or calls across a DbContext which may already be tracking entity references from previous operations / iterations.
Edit: Working with detached references can be problematic and you need to take extra care when doing so. My general recommendation is to avoid it wherever possible. The approach I recommend when dealing with entities is that you should never pass an entity outside of the scope of the DbContext that read it. This means leveraging a ViewModel or DTO to represent entity-sourced details outside the scope of the DbContext. A detached EF Entity
can certainly work, but with a DTO it is explicitly clear that the data cannot be confused with a tracked entity. When it comes to performing operations like a Delete, you only really need to pass the ID.
For example, leveraging Automapper to help translate between DTOs and entities:
PersonDTO AddPerson(PersonDTO details)
{
if(details == null)
throw new ArgumentNullException("details");
using (var context = new AppDbContext())
{
// TODO: Add validations such as verifying unique name/dob etc.
var person = Mapper.Map<Person>(details); // Creates a new Person.
context.Persons.Add(person);
context.SaveChanges();
details.PersonId = person.PersonId; // After SaveChanges we can retrieve the new row's ID.
return details;
}
}
PersonDTO UpdatePerson(PersonDTO details)
{
if(details == null)
throw new ArgumentNullException("details");
using (var context = new AppDbContext())
{
var existingPerson = context.Persons.Single(x => x.PersonId == details.PersonId); // Throws if we pass an invalid PersonId.
Mapper.Map(details, existingPerson); // copies values from our DTO into Person. Mapping is configured to only copy across allowed values.
context.SaveChanges();
return Mapper.Map<PersonDTO>(existingPerson); // Return a fresh, up to date DTO of our data record.
}
}
void DeletePerson(int personId)
{
using (var context = new AppDbContext())
{
var existingPerson = context.Persons.SingleOrDefault(x => x.PersonId == details.PersonId);
if (existingPerson == null)
return; // Nothing to do.
// TODO: Verify whether the current user should be able to delete this person or not. (I.e. based on the state of the person, is it in use, etc.)
context.Persons.Remove(existingPerson);
context.SaveChanges();
}
}
In this example a Person entity does not ever leave the scope of a DbContext. The trouble with detached entities is that whenever passing an entity around to other methods and such, those methods might assume they are working with attached, complete or complete-able (i.e. through lazy loading) entities. Was the entity loaded from a DbContext that is still "alive" so if if the code wants to check person.Address that data is either eager loaded and available, or lazy-loadable? vs. #null which could mean the person does not have an address, or that without a DbContext or lazy loading we cannot determine whether it does or not. As a general rule if a method is written to accept an entity, it should always expect to have a complete, or complete-able version of that entity. Not a detached "maybe complete, maybe not" instance, not a "new"ed up instance of a class that has some arbitrary values populated, (rather than an entity representing a data row) and not a deserialized block of JSON coming from a web client. All of those can be typed as a "Person" entity, but not a Person entity.
Edit 2: "Complete" vs. "Complete-able"
A Complete entity is an entity that has all related entities eager loaded. Any method that accepts a Person should be able to access any property, including navigation properties, and receive the true value. If the Person has an Address, then a #null address should only ever mean that person does not have an address (if that is valid), not "that person does not have an address, or it just wasn't loaded." This also goes for cases where you might have a method that accepts an entity, which you haven't loaded, but want to substitute with a entity class populated with an ID and whatever data you might have on hand. That incomplete "entity" could find itself sent to other methods that expect a more complete entity. Methods should never need to guess at what they receive.
A Complete-able entity is an entity where any related entities within that entity can be lazy loaded if accessed. The consuming method doesn't need to determine whether properties are available or not, it can access Person.Address and it will always get an Address if that person is supposed to have one, whether the caller remembered to eager load it or not.
Where methods are using tightly scoped DbContexts (using()) if you return an entity then there is no way that you can guarantee later down the call-chain that this entity is complete-able. Today you can make the assurance that all properties are eager-loaded, but tomorrow a new relationship could be added leaving a navigation property somewhere within the object graph that might not be remembered to be eager-loaded.
Eager loading is also expensive, given to ensure an entity is "complete", everything needs to be loaded, whether the consumers ever need it or not. Lazy Loading was introduced to facilitate this, however, in many cases this is extremely expensive leading to a LOT of chatter with the database and the introduction of performance costs when the model evolves. Elements like serialization (a common problem in web applications) touch every property by default leading to numerous lazy load calls for every entity sent.
DTOs/ViewModels are highly recommended when data needs to leave the scope of a DbContext as it ensures only the data a consumer needs is loaded, but equally importantly, as a model may evolve, you avoid lazy loading pitfalls. Serializing a DTO rather than an Entity will ensure those new relationships don't come into play until a DTO is updated to actually need that data.
It goes like this:
MyDbContext ctx = new MyDbContext();
IFooRepository repo = new FooRepository(ctx);
var items = repo.GetAvailableItem().ToList(); //this will query all item.sold = false.
// here it returns three rows
foreach(var item in items) {
item.sold = true;
}
repo.commit(); // this will call the SaveChanges() in DbContext
Thread.sleep(10000)
// Now I quickly execute a query in SQL Server Management Studio
// UPDATE Item SET Sold = 0;
var items02 = repo.GetAvailableItem().ToList(); // this will query all item.sold = false.
// here items02 also contains three rows
// HOWEVER, when I watch the value of item.sold in items02, it is all True
Is this the behavior by design?
Why? Is it because DbContext cache the entity and never refresh even if you run the same query again?
UPDATE
Here is the code in my repo:
public IQueryable<Item> GetAvailableItem()
{
var items = from x in DbContext.Item
where x.Sold == 0
select x;
return items;
}
public virtual int Commit()
{
return DbContext.SaveChanges();
}
OK. This is what happening:
Creating a new context.
Loading items from db by calling GetAvailableItem()
Context will load them, and also cache them.
Updating items via context. So: the db rows ARE updated, and the cached versions ARE updated too.
Updating items via pure sql, outside the context (through SSMS). So: the db rows ARE updated. But, since you are using the same context as before, and it has it's own version of items, and there is no way for it to know what's happening outside itself, so the cached version of items, stays how they were: ARE NOT updated.
If you want to your context know the changes outside itself, the easiest way is to create a new context and query again. Another way is to tell context explicity to re-load entities from db by yourContext.Entry<YourEntityType>(entityInstance).Reload();.
My guess is that your DbContext is not up to date with your changes that happened in the Database (you said that you are running an update during the Thread.sleep). DbContext won't pick up these updates (the data is cached).
That's why you want to have your lifescope of your context as short as possible to reduce concurrency. This is an expected behavior.
See this MSDN post
Below is an example code which should demonstrate what I'm trying to achieve:
public static void addRecord(CargoShip ship, DbContext db)
{
// Realistically, in my application this is decided by business logic, - omitted for brevity
long containerTypeIDNewContainer = 5;
Container containerToAdd = new Container()
{
cargoShipBelongingToID = ship.cargoShipID,
containerTypeID = containerTypeIDNewContainer
};
ship.Containers.Add(containerToAdd);
// This line of code is what I want to be able to do
// WITHOUT doing a db.SaveChanges
// ContainerType is a lookup table in the database, each container type has pre-defined
// Height/Width etc
// This throws a null exception, ContainerType object does not exist.
string containerHeight = containerToAdd.ContainerType.Height;
}
I basically want to be able to create a new Container and fill in a foreign key on that object, and be able to access the child of the foreign key as an object in code.
if I call a SaveChanges, it will naturally go and fetch the values for the Container object, but I want to fill in those values/objects without having to do a Savechanges.
edit:
Forgot to mention incase it is not clear. The relationship is: CargoShip has many Container, a Container has ONE ContainerType, which is a FK and is non nullable.
Can you help with this.
Thanks
EF will not fill in all your FK properties for you just because you set an ID in your entity. Those properties will only be set after you call SaveChanges.
For that reason, there's not much point in calling Add unless you do actually intend to call SaveChanges at some point.
If you simply want to get the appropriate ContainerType, then you may as well do a Select (or Find) - which ultimately boils down to much the same SQL query that EF would have to use behind the scenes anyway:
var containerType = db.ContainerTypes.Find(containerTypeIdNewContainer);
Note that the Find method will only go to the DB if the particular containerType record does not already exist in the local cache. However, the lifetime of the local cache is tied to the lifetime of the DbContext, so may be very short.
It may make sense to maintain your own cache of commonly-used entities like this if they're relatively stable:
var containerTypes = db.ContainerTypes.ToDictionary(x => x.Id, x => x);
...
var containerType = containerTypes[containerTypeIdNewContainer];
Assuming you can access your DbContext (not familiar with EntityFrameworkConnection class) then you can simply use an IQueryable.
IQueryable<T> query = db.Set<ContainerType>()
var containerType = query.FirstOrDefault(x => x.containerTypeID == containerTypeIDNewContainer)
string containerHeight = containerType.Height;
Hope I understood your question properly
I have some code (more complex than in this example) where I don't understand why the FirstOrDefault call, which clearly round-trips to DB, is not returning a fresh entity from DB:
var dbContext = new MyDBContext();
// this will not round-trip to Db if found in context
var user = this.dbContext.EFUsers.Find(someUID);
// some other work here with 'user'
this.dbContext.ObjectContext.Detach(user);
// stuff in disconnected mode
var newUser = new EFUser() { UID = someUID };
// stuff in disconnected mode
this.dbContext.EFUsers.Attach(newUser);
// finish working with newUser
// (eg. deletion of many-to-many relation to avoid loading related entities in memory)
user = this.dbContext.EFUsers.FirstOrDefault(us => us.UID == someUID);
// I would expect at this point that 'user' will be queried with fresh values from DB,
// In reality, I get back only the entity with UID filled in, 'newUser' from above
// some update user goes here which fails because of the above
dbContext.SaveChanges();
I thought Find will return me the user from the context, if available, but FirstOrDefault always a user with fresh values from database.
Can anyone explain? How would I overcome this effect?
Worth reading this http://msdn.microsoft.com/en-us/data/jj573936,
"When results are returned from the database, objects that do not
exist in the context are attached to the context. If an object is
already in the context, the existing object is returned (the current
and original values of the object's properties in the entry are not
overwritten with database values)."
the load and reload methods are worth a read too.
Sounds like you want that behaviour.
http://msdn.microsoft.com/en-us/data/jj592911.aspx
http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbentityentry.reload(v=vs.113).aspx
I am trying to update a record using Entity Framework but want to avoid manually setting each property and then calling save changes. I have an instance of this class bound to a dataform that updates all its properties as a user changes them. The nicest way I can see using this would be to set the object in the context equal to the object bound to my dataform but this does not work. I tried removing the object from the context then adding the dataform bound object but it creates a completely new record (I expected this but decided it was worth a shot to try it out). I assume there is some type of flag with in the context that is used to detect if a record is a new row or not considering the fact that replacing the context object with the databound object (which has the same pk value) does not work.
using (var scope = new TransactionScope())
{
try
{
using (var context = new CIS_DEVEntities())
{
GangMemberBio bio = context.GangMemberBios.First(P => P.id == this.id);
bio = this; //this does not work.
context.SaveChanges();
}
//If the top code is successfully completed then the transaction will be commited
//if not this line will be skipped and execution will be given to the catch block
scope.Complete();
}
catch
{
}
}
Edit Idea 1
I was thinking I could create a context object on my wpf window itself and then bind the dataform to my gangmemberbio object retrieved from this context. Then when I call save changes nothing else needs to be done. I heard that having a datacontext in memory is bad though.. Or is it just bad if I use it outside the context of a using statement?
DbContext keeps track of each entity's state internally, so it knows whether to add/update/delete any given one. When you Add() an entity to the context, even though it might already have an id, it's default state is Added (might be New, not sure), which causes the existing id to be ignored and a new record inserted into the db.
What you want to do is attach the already existing entity to the DbContext, and then let the DbContext know that it's to be treated as an existing entity so that it's updated, rather than inserted.
You do that by setting the EntityState in the DbContext for the given entity to Modified (by default it's New when adding an entity to the DbContext).
Try something like this:
using (var context = new CIS_DEVEntities())
{
context.Entry(this).State = EntityState.Modified;
context.SaveChanges();
}
In your edit page, load the object from the database first which will cause your form's fields to all be populated with existing data from the database. When the form is posted then, all the values of your data object model will be set.