Entity framework keeps inserting duplicate objects when adding - entity-framework

I could really need some help in order to solve this issue. When I try to add an entity using Entity Framework, it keeps adding 1 more than needed.
Here you see my database after I have added 2 movies.
As you see, it adds the same movie "The rock" twice.
Been looking into the problem the past two days, but haven't found a solution that don't giving my exceptions.
Code:
public bool Execute(RequestedMovie movie)
{
using (var context = new MoviesContext())
{
context.RMovies.Attach(movie);
context.RMovies.Add(movie);
context.SaveChanges();
}
return true;
}
Model:
public class RequestedMovie
{
[Key]
public int RequestedMoviesID { get; set; }
public string MovieId { get; set; }
public string MovieTitle { get; set; }
public string MovieLink { get; set; }
public string MovieYear { get; set; }
public int MovieQuality { get; set; }
public string Requester { get; set; }
public bool Status { get; set; }
}
DataContext:
public class MoviesContext : DbContext, IMoviesContext
{
public MoviesContext() : base("MoviesContext")
{
}
// DbSet to bookings
public DbSet<Movie> Movies { get; set; }
public DbSet<RequestedMovie> RMovies { get; set; }
public void MarkAsAdded(Movie item)
{
Entry(item).State = EntityState.Added;
}
public void MarkAsDeleted(Movie item)
{
Entry(item).State = EntityState.Deleted;
}
public void MarkRequestedMovieAsAdded(RequestedMovie item)
{
Entry(item).State = EntityState.Added;
}
public void MarkRequestedMovieAsModified(RequestedMovie item)
{
Entry(item).State = EntityState.Modified;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
This should be pretty strait forward, because I only have one table which I'm going to add to. Have tried with the Attach approach that I found in another Stack post, but it still won't work :(.
Have also tried using the methods (MarkRequestedMovieAsAdded) I have in my context file, instead of RMovies.Add(objekt), but same result.
What could be wrong here?

Just use:
context.RMovies.Add(movie);
context.SaveChanges();

I managed to solve this issue. I haven't done any mistake in the Web Api. It turned out that my Angular2 observable calls made an error and called my web api twice because it was (cold) and not (warm).
Here is the post about it:
Angular2 http.post gets executed twice
All I should do was add .share() after mapping in my angular2 service.

Related

Asp.net core 2.0 The instance of entity type 'X' cannot be tracked because another instance with the key value 'Id:x' is already being tracked

I have been working on a shop site project, using asp.net core spa templates provided with the latest VS2017, and have come across an issue that I haven't had before, possibly because until now my apps were quite simple!
I know what the problem is and where, I just can't fix it. I have a product model which has a collection of "Attributes" and a collection of "Variations" (different colour size, etc) and those variations also have attributes, so if the same Attribute shows up in the Variation (VAttributes), as is already in the main "Attributes" I get the error
InvalidOperationException: The instance of entity type
'ProductAttribute' cannot be tracked because another instance with the
key value 'Id:2' is already being tracked. When attaching existing
entities, ensure that only one entity instance with a given key value
is attached.
The best answer I found was here : https://stackoverflow.com/a/19695833/6749293
Unfortunately, even with the above check I got the error, I even tried making a list of attached attributes, and if the vattribute matched one of the items in the list, I didn't attach it. In fact I found that even if I don't attach (_context.attach()) any of the vAttributes, it still throws the error!.
Here's the code in question:
public async Task<Product> Create(Product product)
{
try
{
foreach (var variation in product.Variations)
{
foreach (var vAttr in variation.VAttributes)
{
bool isDetached = _context.Entry(vAttr).State == EntityState.Detached;
if (isDetached)
_context.Attach(vAttr);
}
}
foreach (var attribute in product.Attributes)
{
bool isDetached = _context.Entry(attribute).State == EntityState.Detached;
if (isDetached)
_context.Attach(attribute);
}
foreach (var category in product.Categories)
{
_context.Attach(category);
_context.Attach(category).Collection(x => x.Children);
}
_context.Products.Add(product);
await Save();
return product;
}
catch (Exception)
{
throw;
}
}
The models for the 3 objects are as follows:
public class Product
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public string StockRef { get; set; }
public DateTime? LastModified { get; set; }
//image needed
public ICollection<ProductCategory> Categories { get; set; }
public ICollection<ProductAttribute> Attributes { get; set; }
public ICollection<ProductVariation> Variations { get; set; }
public Product()
{
Attributes = new List<ProductAttribute>();
Variations = new List<ProductVariation>();
Categories = new List<ProductCategory>();
}
}
Variation:
public class ProductVariation
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<ProductAttribute> VAttributes { get; set; }
//needs images
public decimal VPrice { get; set; }
public string VStockRef { get; set; }
}
Finally the Attribute:
public class ProductAttribute
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("AttributeCategory")]
public int AttributeCategoryId { get; set; }
public virtual AttributeCategory AttributeCategory { get; set; }
}
Most help I found when searching was more related to having repo's injected as singletons, or HttpPut methods where the code had check for existence omitting the .AsNoTracking() or it was a mistake that they had the second instance in some way, where I am aware of the second instance, I just don't know how to prevent it from being tracked!
EDIT: I found that adding a foreign key on the ProductVariation model to the Product that was being created failed as it was only a temp key!? anyway removed it from the variation model, so have updated my code. Also thought I'd add one of my earler failed attempts, that led to all of the foreach loops.
_context.AttachRange(product.Attributes);
_context.AttachRange(product.Categories);
_context.AttachRange(product.Variations);
_context.Add(product);
I believe you can allow EF to handle the tracking.
public virtual bool Create(T item)
{
try
{
_context.Add(item);
_context.SaveChanges();
return true;
}
catch (Exception e)
{
return false;
}
}
This allows for you to save the entire object structure without worring about attaching items.
var newProduct = new Product();
newProduct.Categories.Add(cat);
newProduct.Attributes.Add(att);
newProduct.Variations.Add(vari);
Create(newProduct);

Entity framework replaces delete+insert with an update. How to turn it off

I want to remove a row in database and insert it again with the same Id, It sounds ridiculous, but here is the scenario:
The domain classes are as follows:
public class SomeClass
{
public int SomeClassId { get; set; }
public string Name { get; set; }
public virtual Behavior Behavior { get; set; }
}
public abstract class Behavior
{
public int BehaviorId { get; set; }
}
public class BehaviorA : Behavior
{
public string BehaviorASpecific { get; set; }
}
public class BehaviorB : Behavior
{
public string BehaviorBSpecific { get; set; }
}
The entity context is
public class TestContext : DbContext
{
public DbSet<SomeClass> SomeClasses { get; set; }
public DbSet<Behavior> Behaviors { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<SomeClass>()
.HasOptional(s => s.Behavior)
.WithRequired()
.WillCascadeOnDelete(true);
}
}
Now this code can be executed to demonstrate the point
(described with comments in the code below)
using(TestContext db = new TestContext())
{
var someClass = new SomeClass() { Name = "A" };
someClass.Behavior = new BehaviorA() { BehaviorASpecific = "Behavior A" };
db.SomeClasses.Add(someClass);
// Here I have two classes with the state of added which make sense
var modifiedEntities = db.ChangeTracker.Entries()
.Where(entity => entity.State != System.Data.Entity.EntityState.Unchanged).ToList();
// They save with no problem
db.SaveChanges();
// Now I want to change the behavior and it causes entity to try to remove the behavior and add it again
someClass.Behavior = new BehaviorB() { BehaviorBSpecific = "Behavior B" };
// Here it can be seen that we have a behavior A with the state of deleted and
// behavior B with the state of added
modifiedEntities = db.ChangeTracker.Entries()
.Where(entity => entity.State != System.Data.Entity.EntityState.Unchanged).ToList();
// But in reality when entity sends the query to the database it replaces the
// remove and insert with an update query (this can be seen in the SQL Profiler)
// which causes the discrimenator to remain the same where it should change.
db.SaveChanges();
}
How to change this entity behavior so that delete and insert happens instead of the update?
A possible solution is to make the changes in 2 different steps: before someClass.Behavior = new BehaviorB() { BehaviorBSpecific = "Behavior B" }; insert
someClass.Behaviour = null;
db.SaveChanges();
The behaviour is related to the database model. BehaviourA and B in EF are related to the same EntityRecordInfo and has the same EntitySet (Behaviors).
You have the same behaviour also if you create 2 different DbSets on the context because the DB model remains the same.
EDIT
Another way to achieve a similar result of 1-1 relationship is using ComplexType. They works also with inheritance.
Here an example
public class TestContext : DbContext
{
public TestContext(DbConnection connection) : base(connection, true) { }
public DbSet<Friend> Friends { get; set; }
public DbSet<LessThanFriend> LessThanFriends { get; set; }
}
public class Friend
{
public Friend()
{Address = new FullAddress();}
public int Id { get; set; }
public string Name { get; set; }
public FullAddress Address { get; set; }
}
public class LessThanFriend
{
public LessThanFriend()
{Address = new CityAddress();}
public int Id { get; set; }
public string Name { get; set; }
public CityAddress Address { get; set; }
}
[ComplexType]
public class CityAddress
{
public string Cap { get; set; }
public string City { get; set; }
}
[ComplexType]
public class FullAddress : CityAddress
{
public string Street { get; set; }
}

check if a property is ignored by EntityFramework

Using EntityFramework 4.3 w/POCOs.
how can I check if a property on a model is ignored or not.
In my DBContext Class Hierarchy I am ignoring a property by
modelBuilder.Entity<EClass>().Ignore (f => f.IgnoredProperty());
In my BaseContext class, I need to check if that property is ignored or not.
private void ProcessGlobalConvention(DbModelBuilder modelBuilder, IGlobalConvention convention)
{
modelBuilder.Entity<typeof(this.GetType())>("Ignored Property");
}
How can I do that?
Thanks
Use the EF power tools http://www.infoq.com/news/2013/10/ef-power-tools-beta4 to view your model. Is the property there?
Create a database. Is the column there?
Look at the Database.LogSqlEvents http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/ and parse the sql to see if the field name appears...
....unless you really want a code solution...?
IN WHICH CASE
New up your DbContext
Create one record and add it to the relevant DbSet
Get the DbEntityEntry
Look in CurrentValues.PropertyNames. Is your property there?
[TestMethod]
public void CreateDatabase()
{
Database.SetInitializer(new DropCreateDatabaseAlways<HomesContext>());
var db = new HomesContext();
Assert.IsFalse(db.Homes.Any());
var home = db.Homes.Create();
db.Homes.Add(home);
var entry = db.Entry(home);
Assert.IsTrue(entry.CurrentValues.PropertyNames.Contains("MaxResidents"));
Assert.IsTrue(entry.CurrentValues.PropertyNames.Contains("MaxStaff"));
Assert.IsFalse(entry.CurrentValues.PropertyNames.Contains("CurrentResidents"));
Assert.IsFalse(entry.CurrentValues.PropertyNames.Contains("CurrentStaff"));
}
public class HomesContext:DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Home>().Ignore(x => x.CurrentResidents);
base.OnModelCreating(modelBuilder);
}
public DbSet<Home> Homes { get; set; }
}
public class Home
{
public int HomeId { get; set; }
public string HomeName { get; set; }
public int MaxResidents { get; set; }
public int MaxStaff { get; set; }
public int CurrentResidents { get; set; }
[NotMapped]
public int CurrentStaff { get; set; }
}

Setting EntityState.Modified during update operation with Entity Framework

Assume that I have the following little console application which uses Entity Framework 5:
class Program {
static void Main(string[] args) {
using (var ctx = new ConfContext()) {
var personBefore = ctx.People.First();
Console.WriteLine(personBefore.Name);
personBefore.Name = "Foo2";
ctx.SaveChanges();
var personAfter = ctx.People.First();
Console.WriteLine(personAfter.Name);
}
Console.ReadLine();
}
}
public class ConfContext : DbContext {
public IDbSet<Person> People { get; set; }
public IDbSet<Session> Sessions { get; set; }
}
public class Person {
[Key]
public int Key { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime? BirthDate { get; set; }
public ICollection<Session> Sessions { get; set; }
}
public class Session {
[Key]
public int Key { get; set; }
public int PersonKey { get; set; }
public string RoomName { get; set; }
public string SessionName { get; set; }
public Person Person { get; set; }
}
As you can see, I am changing the name of the record and saving it. It works but it feels like magic to me. What I am doing in all of my applications is the following one (to be more accurate, inside the Edit method of my generic repository):
static void Main(string[] args) {
using (var ctx = new ConfContext()) {
var personBefore = ctx.People.First();
Console.WriteLine(personBefore.Name);
personBefore.Name = "Foo2";
var entity = ctx.Entry<Person>(personBefore);
entity.State = EntityState.Modified;
ctx.SaveChanges();
var personAfter = ctx.People.First();
Console.WriteLine(personAfter.Name);
}
Console.ReadLine();
}
There is no doubt that the second one is more semantic but is there any other obvious differences?
Well the second code block where you explicitly set the entity state is redundant, as the change tracker already knows that the entity is modified because the context knows about the entity (as you query the context to retrieve the entity).
Setting (or painting) the state of the entity would be more useful when working with disconnected entities, for example in an n-tier environment where the entity was retrieved in a different context and sent to a client for modification, and you wish to mark those changes back on the server using a different context.
Otherwise, the first code block is cleaner in my opinion.

Adding a child object before inserting the parent object and child object

I worked through the Nerd Dinner application. In the Create action method they have the following code:
NerdIdentity nerd = (NerdIdentity)User.Identity;
dinner.HostedById = nerd.Name;
dinner.HostedBy = nerd.FriendlyName;
RSVP rsvp = new RSVP();
rsvp.AttendeeNameId = nerd.Name;
rsvp.AttendeeName = nerd.FriendlyName;
dinner.RSVPs.Add(rsvp);
dinnerRepository.Add(dinner);
dinnerRepository.Save();
I am using Entity Framework 4.1 code first.
Here is my GrantApplication class:
public class GrantApplication
{
public int Id { get; set; }
// Other properties
public virtual ICollection<AuditEntry> AuditEntries { get; set; }
}
In my service layer I do the following, the same as what Nerd Dinner does it:
public void Insert(GrantApplication grantApplication)
{
// Add audit entry
grantApplication.AuditEntries.Add(new AuditEntry
{
NewValue = grantApplication.GrantApplicationStateId,
AuditDate = currentDateTime,
EmployeeNumber = submitterEmployeeNumber
});
// Insert the new grant application
grantApplicationRepository.Insert(grantApplication);
}
My AuditEntry class:
public class AuditEntry
{
public int Id { get; set; }
public int OldValue { get; set; }
public int NewValue { get; set; }
public DateTime AuditDate { get; set; }
public string EmployeeNumber { get; set; }
}
My context class:
public class HbfContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
public DbSet<AccountType> AccountTypes { get; set; }
public DbSet<GrantApplication> GrantApplications { get; set; }
public DbSet<AuditEntry> AuditEntries { get; set; }
protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
{
}
}
I get an error that grantApplication.AuditEntries is null so it can't add the audit entry object. Why is mine null, but dinner.RSVPs is not null when it tries to add the RSVP obkect? How would I fix it?
Do I need to add AuditEntries to HbfContext? I mean I'm not going to use it on it's own. It will only be used when a GrantApplication is edited.
UPDATE
I must be using an older version of Nerd Dinner, but this is what my Create looks like:
[HttpPost, Authorize]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
NerdIdentity nerd = (NerdIdentity)User.Identity;
dinner.HostedById = nerd.Name;
dinner.HostedBy = nerd.FriendlyName;
RSVP rsvp = new RSVP();
rsvp.AttendeeNameId = nerd.Name;
rsvp.AttendeeName = nerd.FriendlyName;
dinner.RSVPs.Add(rsvp);
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
return View(dinner);
}
Um, because the NerdDinner DinnersController.Create includes a line of code which you didn't show?
dinner.RSVPs = new List<RSVP>(); // why is this not in your example?
dinner.RSVPs.Add(rsvp);
Do I need to add AuditEntries to HbfContext?
Yes, you do. Well, you have to add them to your EF model in some way. That's one way to do it. You might be able to do it with code in OnModelCreating as well.