When I create new entities in a context, I cannot get a proxy even if I request it again. I have to dispose of the context and make a new one. Is this an expected behavior or am I doing something wrong?
int id = 0;
using (var context = new MyContext())
{
var A = context.People.Add(new Person{ Name = "Bob" }); // A is not a proxy
context.SaveChanges(); // A is still not a proxy
var B = context.People.Where(o => o.Id == A.Id).Single(); // B is not a proxy
id = A.Id; // Keep the ID to use with a new context
}
using (var context = new MyContext())
{
var C = context.People.Where(o => o.Id == id).Single(); // C is a proxy!
}
You can use the Create method of your DBSet:
var newPerson=context.People.Create();
The instance returned will be a proxy if the underlying context is configured to create proxies and the entity type meets the requirements for creating a proxy.
Update
As #Asad said, if you are creating a new entity, you need to add it to your DBSet after creating it:
context.People.Add(newPerson);
Or change its State to Added, for example:
context.Entry(newPerson).State = EntityState.Added;
And, if you are updating it, then you should use Attach method:
var existingPerson=context.People.Create();
existingPerson.Id = 1; // assuming it exists in the DB
context.People.Attach(existingPerson);
octavioccl's answer is correct but would force me to break my data layer pattern. I have an alternative (probably slower) solution that didn't force me to create the entity inside my repository nor required me to add mapping everywhere (or a mapping library). I am still accepting your answer since it probably is closer to best practice and did answer the original question but I wanted to add this option if ever needed.
dbContext.People.Add(person);
dbContext.SaveChanges();
dbContext.Entry(person).State = EntityState.Detached;
dbContext.People.Find(person.Id);
Related
I have these two tables:
public class FiscalYear
{
... other fields
public int FiscalYears_Id { get; set; }
}
public class SkipHeader
{
... other fields
public int FiscalYears_Id { get; set; }
public virtual FiscalYear FiscalYear { get; set; }
}
Attempting to create a new SkipHeader like so:
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYear = Session.FiscalYear,
}
Will cause the database to create a new FiscalYear record instead of using the Session.FiscalYear which is simply a static property that gets assigned to at program start. However, if I assign the FiscalYears_Id instead:
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
The program uses the existing record as expected.
This bug eluded me and my colleague for months! Now that I found a solution, I would like to know WHY this is the case?
This bug eluded me and my colleague for months! Now that I found a
solution, I would like to know WHY this is the case?
This occurs because the DbContext doesn't know about your FiscalYear object instance, such as whether it represents a new record or an existing one.
Take the following example:
var fiscalYear = new FiscalYear { Id = 4, Name = "2019/20" };
var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
context.SkipHeaders.Add(skipHeader);
context.SaveChanges();
fiscalYear in this instance is an object instance that has been given an ID and Name. When we associate it to a new SkipHeader and add the SkipHeader to the DbContext, EF will see this fiscalYear. Since it isn't an object tracked by the context, it treats it as a new entity like the SkipHeader.
Depending on how your entities are configured for dealing with the PK will determine what happens.
If your PK (Id) is set up as an Identity column (DB will populate) then the FiscalYear will be inserted and assigned the next available Id value. After the SaveChanges() call, fiscalYear.Id would be "6" or "22" or whatever the next new ID assigned to it would be. (Not "4")
If your PK is not an Identity column (App will populate) and a FiscalYear row already exists in the DB for ID 4, then EF will throw a duplicate key Exception on SaveChanges().
Where people get confused is that they assume that since the FiscalYear was at one point (Say during a web request) loaded from a DbContext, it is still somehow acting as a tracked entity when passed into another method outside of the scope of that DbContext. (During another update web request) It's not. When a web request for instance accepts a FinancialYear as a parameter from the client, it is deserializing a FinancialYear. As far as EF is concerned, that is absolutely no different than the new FinancialYear { } example above. The DbContext is not aware of that entity.
Take the following example:
FiscalYear fiscalYear = null;
using (var context = new AppDbContext())
{
fiscalYear = context.FiscalYears.Single(x => x.Id == 4);
}
using (var context = new AppDbContext())
{
var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
context.SkipHeaders.Add(skipHeader);
context.SaveChanges();
}
This provides a basic outline of a Fiscal Year that was loaded by one instance of a DbContext, but then referenced by another instance of a DbContext. When SaveChanges is called, you will get a behaviour like you are getting now. This is what essentially happens in web requests, as when an entity is returned, the entity definition is merely a contract and the Entity is serialized to send to the client. When it comes back into another request, a new untracked object is deserialized.
As a general rule, Entities should not be passed outside the scope of the DbContext they were read from. EF does support this via detaching and re-attaching entities, but this is honestly more trouble than it is typically worth because you cannot 100% rely on just attaching an entity using DbContext.Attach() as often there can be conditional cases where another entity instance is already being tracked by a context and the Attach will fail. In these cases you'd need to replace references with the already tracked entity. (Messy conditional logic to catch possible scenarios) References are everything when dealing with EF. Two different object references with the same key & values are treated as separate and different objects by EF. Rather than passing references around, it's usually a lot simpler, and better to pass just the FK. This has the benefit of being a smaller payload for web requests.
One option you've found out is to update via the FK:
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
This works, however when you have entities that are exposing both FK (FiscalYears_Id) and navigation property (FiscalYear) you can potentially find mismatch scenarios when updating records. This is something to be careful with as an application evolves.
For instance, take an example where you are editing an existing SkipHeader with a FiscalYears_Id of "4". This will have an associated FiscalYear reference available with a PK of "4".
Take the following code:
var skipHeader = context.SkipHeaders.Include(x => x.FiscalYear).Single(x => x.Id == skipHeaderId);
skipHeader.FiscalYears_Id = newFiscalYearId; // update FK from "4" to "6"
var fiscalYearId = skipHeader.FiscalYear.Id; // still returns "6"
context.SaveChanges();
We set the FK value on the skip header, however that does not update the reference for FiscalYear until after we call SaveChanges. This can be an important detail when dealing with FKs alongside navigation properties. Now normally we wouldn't bother going to the Navigation Property to get the ID again, but any code we call that is expecting the new FiscalYear reference to be updated will have a different behavior depending on whether SaveChanges had been called before or after the code in question. If before, all FiscalYear details will be for the old fiscal year even though we changed the FK reference.
This can also lead to odd Lazy Loading errors as well such as:
var skipHeader = context.SkipHeaders.Single(x => x.Id == skipHeaderId);
skipHeader.FiscalYears_Id = newFiscalYearId; // update FK from "4" to "6"
var fiscalYearId = skipHeader.FiscalYear.Id; // NullReferenceException!
context.SaveChanges();
Normally, provided you have lazy loading enabled loading a SkipHeader without eager loading the FiscalYear (.Include(x => x.FiscalYear))and then querying a property from the FiscalYear would lazy load this relative. However, if you change the SkipHeader's FiscalYear_ID FK and then try to access a property off the FiscalYear before calling SaveChanges(), you will get a NullReferenceException on the FiscalYear. EF will NOT lazy load either the old or new FiscalYear entity. Bugs in behaviour like that commonly creep in as applications get developed and code starts calling common functions that assume they are dealing with complete entities.
The alternative to setting updated values for known rows by FK is to load the entity to associate, and associate it by reference:
using (var context = new AppDbContext())
{
var fiscalYear = context.FiscalYears.Single(x => x.Id == fiscalYearId);
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYear = fiscalYear;
}
context.SaveChanges();
}
This example just uses a locally scoped DbContext. If your method has an injected context then use that instead. The context will return any cached, known instance of the Fiscal Year or retrieve it from the DB. If the FiscalYear ID is invalid then that operation will throw an exception specific to the Fiscal Year not being found due to the Single() call rather than a more vague FK violation on SaveChanges(). (Not an issue when there is only one FK relationship, but in entities that have dozens of relationships...)
The advantage of this approach is that the FiscalYear will be in the scope of the DbContext so any methods/code using it will have a valid reference. The entities can define the navigation properties without exposing the extra FK values,using .Map(x => x.MapKey()) [EF6] or Shadow Properties [EFCore] instead to avoid 2 sources of truth for FK values.
This hopefully will provide some insight into what EF is doing and why it resulted in the behaviour you've seen and/or any errors or buggy behaviour you might have also come across.
Assuming you have pretty standard setup with DbContext being scoped (per request) dependency - the reason is that the new instance of your DbContext does not track the Session.FiscalYear instance - so it creates new. Another way to solve this is using DbContext.Attach:
context.Attach(Session.FiscalYear);
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
// save skipHeader
More about change tracker in EF.
I'm having an issue with Entity Framework, when I execute SaveChanges, the context has quite a few objects associated with it, some of the objects are updated and some are added, afterwards I want to use the Id's of all these items (the Id's for the added items are only assigned on insert in the database). After the save changes a list of all the objects is empty.
I've seen samples on the site where the object is updated after the save so I suspect it might be how I'm getting the list of objects in the first place
Here's my code:
// Lots of processing to create or update objects
using (var localContext = this.context)
{
var updatedObjects = localContext.ChangeTracker.Entries().Where(e => e.Entity is GenerationEvent && (e.State == EntityState.Modified || e.State == EntityState.Added));
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity);
// The list has 5 items in at this point
localContext.SaveChanges();
// This list is now empty
DoSomethingWithList(updatedEvents);
}
Thanks in advance for any help.
The variable updatedEvents is a Linq query. Linq queries aren't executed immediately. By the time it is executed in your code it won't find any updated object anymore. Putting .ToList() after the Linq query will execute it immediately.
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity).ToList();
first your "using" statement is odd.
it should be
using (var context = new MyContext()) //passing in optional connection string.
{
}
Then the way you access your entities seem odd or i have no clue what you are doing there...
var updatedObjects = localContext.ChangeTracker.Entries().Where(e => e.Entity is GenerationEvent && (e.State == EntityState.Modified || e.State == EntityState.Added));
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity);
Seems like you are asking the context for all items which are considered "Add" or "Updated"
Then you are accepting the changes to the context. eg SaveChanges().
I fully expect "updatedEvents" to be empty, after save-changes is called.
Change you stuff... to something like
using (var context = new MyContext()) //passing in optional connection string.
{
LIst<EntityType> listOfChangedEntities = //ToDo:either passed in or generated
context.EntityType.AddRandge(listOfChangedEntities);
context.SaveChanges();
//after SaveChanges has been done all the entities in the
//listOfChangedEntities will now have there id's
//for update, fetch the entities... change them and Update them
}
I suspect that you are trying to create some sort of generic code to handle any type of Entity without specifying its type. Your code is not suited for this as it is, if this is what you are trying to do, I would modify the question to ask what you are trying to achieve. But the above is the Normal way of getting the Id's of the entities which have been inserted.
The other examples you are passably talking about is where they use foreign keys and navigation properties to automatically associate related entities, but your code looks way off from that.
UPDATE
routine
public static DoWork()
{
var context = new MyContext();
List<GenerationEvent > internalEntityType = new List<GenerationEvent ();
foreach(var item in SomeList)
{
var newItemEntity = new GenerationEvent();
newItemEntity.Name = "Test";
context.GenerationEvent.Add(newItemEntity);
//add to internal list
internalEntityType.Add(newItemEntity )
}
context.SaveChanges();
var first_id = internalEntityType.FirstOrDefault().Id;
//first_id will not be 0 it will be the Id the database gave it.
}
i've got some trouble with EF (6.0.0)
here is the code
var Answer = new TicketAnswer();
Answer.Answer = "hello";
Answer.TicketId = 20;
Answer.ConfirmDate = DateTime.Now;
db.TicketAnswer.Add(Answer);
db.SaveChanges();
AnswerId = Answer.ID;
db.TicketAnswer.Where(x=> x.ID == AnswerId).FirstOrDefault();
after that , when im trying to get the db.TicketAnswer with the same Id of the Answer (which is new created) EF returning with TicketAnswer class (not a proxy) and i cant access to Ticket class over that (Ticket Property is null even TicketId is not null and there is Ticket on the db which Id = 20 , there is not problem with relations) , but when i change my query to :
var a = db.TicketAnswer.Where(x => x.ID == 225).FirstOrDefault();
EF returning back with System.Data.Entity.DynamicProxies_ASDGAFD... and i can access to Ticket class.
All i want is , reach Ticket class over TicketAnswer class , What should i do ?
Your navigation property has not yet loaded for the newly added entity in that context. To load it you must:
var ticketAnswer = db.TicketAnswer.Include(ta => ta.Ticket).Where(x=> x.ID == AnswerId).FirstOrDefault();
or better:
var ticketAnswer = db.TicketAnswer.Include(ta => ta.Ticket).Single(ta=> ta.Id == answerId);
One may ask "Then why the other entity (with Id == 225) is loaded without using this .Inlcude thing?"
The answer is: That entity was added surely by and other run session, using an other db context instance. So that entity in not in the cache of the current db context instance. When asking for it, EF will load it, and navigation properties are available without explicit Include. However the freshly added entity is in the cache, with no navigation properties. Simple asking for it using where will give you back the very same instance you've added. Note: not just very same entity: the very same instance.
To navigate multiple hops in your entity graph you can use:
.Include("Ticket.User") // In case if the Ticket entity has a navigation property called 'User'
I've a small problem with updating entities if the entity is changed outside the DbContext (is a detached entity). If I attach the modified entity, it's state is not modified.
My code looks like this:
var specificationToSave = GetSpecificationFromTmpStore(userSessionGuid);
using (var context = DataContextFactory.GetDataContext())
{
// this works for update, if I change the values inside the context while debugging
// but it breaks with new entities
context.Specifications.Attach(specificationToSave);
// this works for insert new entities, modified entities will be saved as new entities
context.Specifications.Add((specificationToSave);)
context.SaveChanges();
}
I know NHibernate and it's method SaveOrUpdate. NHibernate decides because of the values if it is updating or inserting the entities.
What is the best practice to do this with EF 4.x and with entities which are modified outside the DbContext?
How can I tell the EF that this entity is in modified state?
If you use the Attach approach on an entity which has already changed, you will also need to tell EF that the entity is modified, after attaching it.
context.Specifications.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
An alternative is to fetch (with tracking), then update the fields, and save:
var entity = context.Specifications.First(s => s.Id == 1234);
entity.Name = "Foo";
... other changes here
context.SaveChanges();
Another option is to make the changes to the entity after you have reattached it, e.g. as per here
context.Specifications.Attach(entity);
entity.Name = "Foo";
... other changes here
context.SaveChanges();
Edit
You can use generics with DbSet - either class, or method - as follows:
public void Update<TEntity>(TEntity entity)
{
DbContext.Set<TEntity>().Attach(entity);
DbContext.Entry(entity).State = EntityState.Modified;
DbContext.SaveChanges();
}
Edit : For updating of detached Parent / Child Graphs
For updating of simple / shallow parent-child relationships where efficiency and performance is not important, simply deleting all old children and reinserting the new ones is an easy (although ugly) solution.
However, for a more efficient scenario requires us to traverse the graph, detect changes, and then add newly inserted, update existing, ignore unchanged, and delete removed items from the Context.
Slauma shows a great example of this here.
You might want to look at using GraphDiff, which can do all this leg work for you!
For disconnected entities, I found this solution.
For finding changes on an existing entity:
var existing = context.Find<Item>(1);
if (existing != null)
{
context.Entry(existing).CurrentValues.SetValues(changed);
}
Its EntityState will be Modified afterwards but only where there are actual changes.
Full example I did in a unit/integration test:
await using var context1 = new MyContext(new DbContextOptionsBuilder().UseSqlite("Data Source=demo.db").Options);
await context1.Database.EnsureDeletedAsync();
await context1.Database.EnsureCreatedAsync();
await context1.Items.AddAsync(new Item
{
Id = 1,
Name = "Something to start with"
});
await context1.SaveChangesAsync();
await using var context2 = new MyContext(new DbContextOptionsBuilder().UseSqlite("Data Source=demo.db").Options);
var existing = context2.Find<Item>(1);
var entry = context2.Entry(existing);
entry.CurrentValues.SetValues(new Item
{
Id = 1,
Name = "Something to start with"
});
entry.State.Should().Be(EntityState.Unchanged);
entry.CurrentValues.SetValues(new Item
{
Id = 1,
Name = "Updated now."
});
entry.State.Should().Be(EntityState.Modified);
Using Microsoft.EntityFrameworkCore.Sqlite and FluentAssertions.
( first of all sorry for the bad english )
I am new to the entity framework and I do a little bit of testing. Lets assume I've got 2 Objects that are POCO Objects. Person and Address.
My scenario is that I want to add a new Person to the DbContext that has a reference to an already existing Object. The connection between both entities is a Many : Many relation.
using ( var t = new Tww.SV.Models.Model.Portal.SVPortalEntities() )
{
testaddress = ( from c in t.Adresses
select c ).ToList().FirstOrDefault();
}
var newPerson = new Person();
newPerson.Name = "Henry";
newPerson.Adresses.Add( testaddress );
using ( var k = new Tww.SV.Models.Model.Portal.SVPortalEntities() )
{
k.Persons.Add(newPerson);
k.SaveChanges();
}
The Problem I do now have is that an additional Address ( with same values but a new Key ) will be created once the Person is added. How can I add the existing reference to the new object instead of creating a new one ?
Use one context for the whole operation:
using ( var t = new Tww.SV.Models.Model.Portal.SVPortalEntities() )
{
testaddress = ( from c in t.Adresses
select c ).ToList().FirstOrDefault();
var newPerson = new Person();
newPerson.Name = "Henry";
newPerson.Adresses.Add( testaddress );
k.Persons.Add(newPerson);
k.SaveChanges();
}
If you can't do that, then you need to manually change the state of the existing address in the new context via the ObjectStateManager or similar.
First of all, do you need to do this within two separate instances of the data context? I assume you do and that you're just providing a simplified example, but if not, then using the same context instance across the entire code would solve your problem.
However, this is a common problem, particularly when using EF classes across services like WCF. The best solution I've found is to "fix up" the incoming new objects before saving them. I'll be the first to admin I find this "ugly" but I also have failed to find any better options that actually work all the time. I usually put the reference re-association code into a method in another partial class segment of my entity class:
public void FixUp(EntityContext c)
{
for (int i = 0; i < this.Addresses.Count; i++)
{
var existing = c.Addresses.SingleOrDefault(a => a.Id = this.Addresses[i].Id);
if (existing != null)
{
this.Addresses[i] = existing;
}
}
}
using (var k = new EntityContext())
{
newPerson.FixUp(k);
k.Persons.Add(newPerson);
k.SaveChanges();
}
To explain why this happens: You are creating two DbContexts. In the first, you retrieve an adress object. Then, outside of any Context scope (see your using ) you are creating a new detached person. It does not belong to either of your two contexts. The next thing that happens is that you create a new context and add your Person object to it.
This context now does not know your new Person (it was "out of scope", or Detached) nor your Adress (It is from another context). Both entities will be added and marked as such in the ChangeTracker, Added.
You have multiple solutions:
First, use only one context to retrieve your Adress, create your Person in and Add it. You need to change your code a bit for this and it might not work out for you if you are working on a service based scenarios.
Second, if you have no chance of changing this code and you need to attach detached objects, like you do in your code snippet, you might want to override SaveChanges of your Context class, and iterate through the entities in the ChangeTracker. Those that are already saved (Id > 0) may be set to "Unchanged" or, if you want to persist eventually made changes, "Modified". This feels ugly, but it should work.