C# Entity Framework - IEntityChangeTracker issue - c#

The exception I am receiving is An entity object cannot be referenced by multiple instances of IEntityChangeTracker. My code is structured like so...
My context class looks like this:
public class MyContext : DbContext, IDataContext
{
public MyContext (string connectionString) :
base(connectionString)
{
}
public DbSet<AssigneeModel> Assignees { get; set; }
public DbSet<AssetAssignmentModel> AssetAssignments { get; set; }
}
public class AssigneeController : Controller
{
protected MyContext db = new MyContext(ConnectionString);
[HttpPost]
public ActionResult Import(SomeObjectType file)
{
AssigneeModel assignee = new AssigneeModel();
assignee.FirstName = "Joe";
assignee.LastName = "Smith";
// Assignees have assets, and the relationship is established via an AssetAssignmentModel entity
AssetAssignmentModel assetAssignmentModel = new AssetAssignmentModel
{
Asset = someExistingAsset,
// Assignee = assignee, // Don't establish relationship here, this object will be added to the assignee collection
}
assignee.AssetAssignments.Add(assetAssignmentModel); // Manually add object to establish relationship
db.Assignees.Add(assignee); // Add the assignee object
// Exception occurs when adding the object above
};
}
EF Version 4.1

The problem is from your Asset object, when you're getting it from the other method, you'll need to explicitly detach it from that context, before adding it to this new context. As Julie mentioned, the entity instance will carry the context with it, but the porblem wasn't with the AssigneeModel you created, but with the someExistingAsset you retrieved.

You've tagged this as EF4.1 (where I expected code first & dbcontext) but it looks like a side effect of EntityObject (edmx, objectcontext, default code gen in VS2008 & VS2010).
In that case, if you have an entity (that derives from EntityObject) and you dispose its' context without first detaching the entity, the entity instance still has an artifact of that context. So when you try to attach it to another context, it gives this message. THat was a problem with EF 3.5 and EF4 if you aren't using POCOs. I haven't had to wrestle with it in a long time but I remember the sting. :)

Related

Entity Framework core - The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked

Am currently trying out my hand in .net core and EF core.
I have the following code to update the data
protected void Update(T entity, bool saveChanges = true)
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
if (saveChanges) _context.SaveChanges();
}
The class I am updating
public partial class Blog
{
public Blog()
{
Post = new HashSet<Post>();
}
public int BlogId { get; set; }
public string Url { get; set; }
public virtual ICollection<Post> Post { get; set; }
}
When I try to update any entry the first time, it is successful. The second time I update the same entry, I get the below error:
System.InvalidOperationException: 'The instance of entity type 'Blog' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.'
The same occurs for each entry, successful for the first time and fails the second time.
Additional Information:
I get all the data using the below code:
protected IEnumerable<T> GetAll()
{
return _context.Set<T>().AsNoTracking().AsEnumerable();
}
Display the above as a table in the view. DTO's are being used to communicate between the data and web layer.
The context is registered in startup as below
services.AddDbContext<BloggingContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
My question is why there is an error during the second update and how to resolve it. Thanks.
Your Service Provider is static and so it is a singleton in fact.
As suggested here
github.com/aspnet/EntityFramework/issues/2652
that exception means that you are trying to attach two entity instances that have the same key to a context. This occurs when a singleton instance of a repository is injected at startup.
I d suggest to change your generic Repository from an abstract class to an interface , and inject the proper repositories during the Startup:
services.AddScoped<IRepository<Blog>, BlogRepository>();
services.AddScoped<IRepository<Post>, PostRepository>();
instead of your static ServiceProvider which actually provides a singleton implementation of your BlogRepository

MigrationHistory as entity in DbContext in Entity Framework 7

I'm trying to get the (by Entity Framework generated) table __EFMigrationsHistoryas an entity into my database context:
In Entity Framework 6 this worked fantastically, but did anyone tried it in EntityFramework 7?
What I tried is, to just add a new class __EFMigrationsHistory - but it'd be too easy - there is already a table named __EFMigrationsHistory in the database (thanks...)
Then I read about that HistoryRow, I shall inherit a class from. So I created the class
public class MigrationsHistory : HistoryRow
{
//... constructor etc.
}
started a migration and: I got two migration-history-tables (but only the original one works)
So I read on and searched for interfaces and classes to implement/inherit from. I found SqlServerHistoryRepository - looks nice. I created an new database context inheriting from SqlServerHistoryRepository (like I'd have done it in EF6). - But this is not a context-class, so I can not add it in Startup.cs (like I did with my default applicationcontext)
So I checked the DbContext for maybe add the history-table somewhere (like in EF6), but there is nothing for adding the table to my context.
So: Anyone already tried to add the migration-history to his context? Anyone was successful?
Whilst I experimented with bricelam's answer with no success with EF Core 2.0,
in
Microsoft.EntityFrameworkCore there's a method;
// .NET Core 1.0 + Platform Extensions
// Microsoft.EntityFrameworkCore.Relational, Version=1.1.0.0, PublicKeyToken=adb9793829ddae60
namespace Microsoft.EntityFrameworkCore
{
public static class RelationalDatabaseFacadeExtensions
{
public static IEnumerable<string> GetAppliedMigrations(this DatabaseFacade databaseFacade);
}
}
Call it as follows;
var dbcontext = new DbContext();
IEnumerable<string> history = dbcontext.Database.GetAppliedMigrations();
Mapping to the table by name should work (it is that easy), you just need to ensure you don't try to re-create it (e.g. by accidentally calling DbContext.Database.EnsureCreate())
class MyContext : DbContext
{
public DbSet<AppliedMigration> AppliedMigrations { get; set; }
}
[Table("__EFMigrationsHistory")]
class AppliedMigration
{
[Column("MigrationId")]
public string Id { get; set; }
[Column("ProductVersion")]
public string EFVersion { get; set; }
}

PK Violation (of other entity) when adding inserting new entity and setting a reference to other entity

I am using Entity Framework 4.3 and I am trying to reference an existing entity by setting the navigation property when creating a new entity, however when i call save EF complains that there is a PK violation in the table for which i set the navigation property to (i.e. it is creating a new record as opposed to a reference!).
How can i attach to an existing POCO as opposed to referencing it and having EF trying to create a new database record (but not simply use an ID, ideally i would like to reference an actual entity that came from another query)?
Thanks in advance,
Chris
public class BusinessUnit
{
public int BusinessUnitID { get; set; }
public ExternalPlugin AccountsDataSourceModule { get; set; }
public ExternalPlugin OptionalContactsDataSourceModule { get; set; }
}
public BusinessUnit NewBusinessUnit(string name, ExternalPlugin accountsModuleId = null, ExternalPlugin contactsModuleId = null)
{
IUnitOfWork unitOfWork = UnitOfWorkFactory.CreateUnitOfWork();
BusinessUnit unit = new BusinessUnit();
unit.CompanyName = name;
unit .AccountsDataSourceModule = accountsModuleId; // this causes a problem
unit .OptionalContactsDataSourceModule = contactsModuleId; // as does this
unitOfWork.BusinessUnitRepository.Insert(unit);
unitOfWork.Save();
return unit;
}
You must attach the existing entities to the context:
BusinessUnit unit = new BusinessUnit();
unit.CompanyName = name;
unitOfWork.ExternalPluginRepository.Attach(accountsModuleId);
unitOfWork.ExternalPluginRepository.Attach(contactsModuleId);
unit.AccountsDataSourceModule = accountsModuleId;
unit.OptionalContactsDataSourceModule = contactsModuleId;
unitOfWork.BusinessUnitRepository.Insert(unit);
...where unitOfWork.ExternalPluginRepository.Attach(ExternalPlugin plugin) must do:
context.ExternalPlugins.Attach(plugin);
I expect that all repositories use the same context instance. Attach tells EF that the plugins already exist in the database and avoids an INSERT of those entities.
Edit
If you get the error message...
An entity object cannot be referenced by multiple instances of
IEntityChangeTracker.
...it means that you have an entity that is attached to more than one context instance at the same time. You can avoid that in most cases by disposing the context always when you don't need it anymore. Your code sample does not follow this good practice. It rather should look like this:
public BusinessUnit NewBusinessUnit(string name,
ExternalPlugin accountsModuleId = null,
ExternalPlugin contactsModuleId = null)
{
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.CreateUnitOfWork())
{
BusinessUnit unit = new BusinessUnit();
unit.CompanyName = name;
unitOfWork.ExternalPluginRepository.Attach(accountsModuleId);
unitOfWork.ExternalPluginRepository.Attach(contactsModuleId);
unit.AccountsDataSourceModule = accountsModuleId;
unit.OptionalContactsDataSourceModule = contactsModuleId;
unitOfWork.BusinessUnitRepository.Insert(unit);
unitOfWork.Save();
return unit;
}
}
At the end of the using block the Dispose method of the unitOfWork is called automatically. To get this working (and compiling as well) you need to derive your IUnitOfWork interface from IDisposable and implement it in the concrete UnitOfWork class:
public interface IUnitOfWork : IDisposable
{
// ...
}
public class ConcreteUnitOfWork : IUnitOfWork, IDisposable
{
private MyDbContext _context;
// I assume that you have a member for the DbContext in this class
// ...
// implementation of IDisposable
public void Dispose()
{
if (_context != null)
_context.Dispose();
}
}

EF keeps trying to persist invalid object

I'm having a rather strange issue with Entity Framework 4.3 in my MVC application. I'm using a Unit of Work wrapper around DbContext, and in my MVC application I use Unity to pass this UOW to my repositories, and repositories to controllers. I've registered the UOW type with the HierarchicalLifetimeManager.
When I try to persist an entity to the database that raises an error, e.g. the database throws a UNIQUE constraint violation, the entity is kept inside EF's ObjectStateManager. So when I go back in my application to fix the error and save the new entity (without errors), EF first tries to add the old and invalid object again, thus failing with the same error.
What am I missing here? I believe that the invalid object should be completely forgotten by EF and that this would be done automatically. But it's clearly not the case.
To add objects to DbContext in order to persis them, the following command gets called (where base is the DbContext):
base.Set<TEntity>().Add(objectToPersist);
And to commit the changes to the database, I call:
base.SaveChanges();
Which throws the error.
I believe that the invalid object should be completely forgotten by EF
and that this would be done automatically. But it's clearly not the
case.
Right, that's not the case and I've never heard that entities would be detached from the context automatically when an exception occured.
There are basically two options to deal with the problem. I show a simple model with your example of a unique key constraint violation:
public class Customer
{
// so we need to supply unique keys manually
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
var customer = new Customer { Id = 1, Name = "X" };
ctx.Customers.Add(customer);
ctx.SaveChanges();
}
// Now customer 1 is in database
using (var ctx = new MyContext())
{
var customer = new Customer { Id = 1, Name = "Y" };
ctx.Customers.Add(customer);
try
{
ctx.SaveChanges();
// will throw an exception because customer 1 is already in DB
}
catch (DbUpdateException e)
{
// customer is still attached to context and we only
// need to correct the key of this object
customer.Id = 2;
ctx.SaveChanges();
// no exception
}
}
}
}
The above is the prefered solution: Correct the object which is attached to the context.
If you need - for whatever reason - to create a new object you must detach the old object from the context. That object is still in state Added and EF will try to save the object again when you call SaveChanges leading to the same exception as before.
Detaching the old object would look like this:
try
{
ctx.SaveChanges();
// will throw an exception because customer 1 is already in DB
}
catch (DbUpdateException e)
{
ctx.Entry(customer).State = EntityState.Detached;
// customer is now detached from context and
// won't be saved anymore with the next SaveChanges
// create new object adn attach this to the context
var customer2 = new Customer { Id = 2, Name = "Y" };
ctx.Customers.Add(customer2);
ctx.SaveChanges();
// no exception
}
This procedure can be tricky if relationships are involved. For example if customer has a relationship to a list of orders, detaching the customer object will delete references between customer and its orders if the orders are attached to the context as well. You have to reestablish the relationships with the new customer2.
Therefore I'd prefer to modify the attached object to put it into correct state. Or let the application crash because such constraint violations usually indicate bugs in the code or - in a multiuser environment - should be handled with proper optimistic concurrency checks.
looks like you'll have to tell EF that you changed your mind about the invalid object:
base.Set().Remove(objectToPersist);
If you want to reset the changes, you could set the ObjectContext to null and re-instantiate it.

Entity Framework RC1 DbContext query issue

I'm trying to implement the repository pattern using entity framework code first rc 1. The problem I am running into is with creating the DbContext. I have an ioc container resolving the IRepository and it has a contextprovider which just news up a new DbContext with a connection string in a windsor.config file. With linq2sql this part was no problem but EF seems to be choking. I'll describe the problem below with an example. I've pulled out the code to simplify things a bit so that is why you don't see any repository pattern stuff here. just sorta what is happening without all the extra code and classes.
using (var context = new PlssContext())
{
var x = context.Set<User>();
var y = x.Where(u => u.UserName == LogOnModel.UserName).FirstOrDefault();
}
using (var context2 = new DbContext(#"Data Source=.\SQLEXPRESS;Initial Catalog=PLSS.Models.PlssContext;Integrated Security=True;MultipleActiveResultSets=True"))
{
var x = context2.Set<User>();
var y = x.Where(u => u.UserName == LogOnModel.UserName).FirstOrDefault();
}
PlssContext is where I am creating my DbContext class. The repository pattern doesn't know anything about PlssContext. The best I thought I could do was create a DbContext with the connection string to the sqlexpress database and query the data that way. The connection string in the var context2 was grabbed from the context after newing up the PlssContext object. So they are pointing at the same sqlexpress database.
The first query works. The second query fails miserably with this error:
The model backing the 'DbContext'
context has changed since the database
was created. Either manually
delete/update the database, or call
Database.SetInitializer with an
IDatabaseInitializer instance. For
example, the
DropCreateDatabaseIfModelChanges
strategy will automatically delete and
recreate the database, and optionally
seed it with new data.
on this line
var y = x.Where(u => u.UserName == LogOnModel.UserName).FirstOrDefault();
Here is my DbContext
namespace PLSS.Models
{
public class PlssContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Corner> Corners { get; set; }
public DbSet<Lookup_County> Lookup_County { get; set; }
public DbSet<Lookup_Accuracy> Lookup_Accuracy { get; set; }
public DbSet<Lookup_MonumentStatus> Lookup_MonumentStatus { get; set; }
public DbSet<Lookup_CoordinateSystem> Lookup_CoordinateSystem { get; set; }
public class Initializer : DropCreateDatabaseAlways<PlssContext>
{
protected override void Seed(PlssContext context)
{
I've tried all of the Initializer strategies with the same errors. I don't think the database is changing. If I remove the
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
Then the error returns is
The entity type User is not part of the model for the current context.
Which sort of makes sense. But how do you bring this all together?
That is correct behavior. Plain DbContext has no idea about mappings (= doesn't know any of your entities). That is the reason why you should always create derived context. Your repository doesn't know about PlssContext but you can still inject it like:
public class Repository
{
private readonly DbContext _context;
public Repository(DbContext context)
{
_context = context;
}
...
}
var repository = new Repository(new PlssContext());
You can't use base DbContext instance directly when using code first.

Categories