3

When I try to insert a entity into the database that has a association with another entity which is already in the database and has an id the entityframework the associated entity gets inserted too. This causes a duplicate entry for the associated entity.

Insert method in the Repository class

public T Insert(T entity)
{
   DbSet.Add(entity);
   Context.SaveChanges();
   return entity;
}

Call of the insert method

this happens somewhere in my Code. I save it into my session variable.

using(var repository  = new Repository<User>())
{
    user = repository.GetById(id);
}

Then some other place:

Post post = new Post{ User = user, Content ="oO" };

using (var rep = new Repository<Post>())
{
    rep.Insert(post);
}

I resolved the duplicate insert with this snippet below. This there a better way than to cast for every entitytype and reattach the assocated entites?

if (entity is Post)
{
     Post post = (Post)(object)entity;
     Context.Users.Attach(post.User);

}
Florian
  • 1,827
  • 4
  • 30
  • 62
Lukas Eichler
  • 5,689
  • 1
  • 24
  • 43
  • I agree, this requires code. But from the top of my head I'd say you added the existing associated entity from a different object context... – Roman Gruber Nov 23 '13 at 18:15
  • Show us a simple but complete working example of your approach. – Silvermind Nov 23 '13 at 18:32
  • Why does Entity Framework Reinsert Existing Objects into My Database? msdn.microsoft.com/en-us/magazine/dn166926.aspx – Colin Nov 25 '13 at 10:47

2 Answers2

0

Most probably your repositories create a new instance of the context instead of sharing one. That's why the newly created context in the Post repository doesn't "see" the user object instance.

Fix this properly by controlling the lifetime of your context and injecting the same instance into different repositories in the same unit of work.

Edit: Container based injection could help you a lot but start by having the data context as the required constructor parameter of your repositories. Then, think of the lifetime of your context.

In a web application a request lifetime is usually most convenient. In a WPF application you could possibly have a view model lifetime, a new context in each view model.

And whether or not the dependency will be satisfied by a container is another story.

Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • Could you show an example please? Should my application handle this single instance or is there something like a container based injection of the context? – Lukas Eichler Nov 23 '13 at 19:35
0

These generic repositories are out. There are numerous reasons not to use them. This is one of them. The repositories suggest that you're obeying the single responsibility principle, but they don't. They contain the "Trojan horse" DbContext, which is a component that spills out far more responsibilities than a generic repository should have. Thus, when a repository saves its "own" entities, any old moment it steps out of line by saving others too.

Make your life much easier by throwing out this useless layer. A DbSet is a repository and a DbContext is a unit of work. (I'm only quoting here). In the EF architecture the UoW contains the repositories and these repositories don't have save responsibilities! The UoW has.

Dropping these generic repositories, you can simply do

using(var db = new MyContext())
{
    var user = db.Users.Find(id);
    Post post = new Post{ User = user, Content ="oO" };
    db.Posts.Add(post); // User is not added because it is known to the context.
    db.SaveChanges();
}

This is a dedicated method that saves a post connected to a user. There is no point trying to make some generic method for this, because it is a specific task (or use case). As you already noticed, when trying to generalize such tasks you probably can't do without inspecting objects and introducing ugly and clunky if or switch chains.

Community
  • 1
  • 1
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291