1

I'm building a feedback functionality. The feedback is supposed to have multiple categories and it should be possible to find feedback based on a category so there is a many to many relationship.

I've set up the following code for this, its designed as code first.

The feeback item:

public class FeedbackItem
{
    public FeedbackItem()
    {

    }
    [Key]
    public long Id { get; set; }

    public virtual ICollection<FeedbackCategory> Categorys { get; set; }
    //public 
    public string Content { get; set; }
    public bool Notify { get; set; }
    public string SubscriptionUserName { get; set; }
    public string SubscriptionUserEmail { get; set; }
    public long SubscriptionId { get; set; }
}

The feedback category:

public class FeedbackCategory
{

   [Key]
   public int Id { get; set; }
   public string Value { get; set; }

   public virtual ICollection<FeedbackItem> Feedbacks { get; set; } 
}

The Database Context:

public class FeedbackContext : DbContext, IFeedbackContext
{
    public FeedbackContext() : base("DefaultConnection")
    {
        //Database.SetInitializer<FeedbackContext>(new FeedbackContextDbInitializer());
    }
    public DbSet<FeedbackItem> FeedbackItems { get; set; }
    public DbSet<FeedbackCategory> Categories { get; set; } 
}

And the Initializer

class FeedbackContextDbInitializer : DropCreateDatabaseAlways<FeedbackContext>
{
    protected override void Seed(FeedbackContext context)
    {
        IList<FeedbackCategory> categories = new List<FeedbackCategory>()
            {
                new FeedbackCategory() { Value = "Android" },
                new FeedbackCategory() { Value = "API" }
            };

        foreach (var feedbackCategory in categories)
        {
            context.Categories.Add(feedbackCategory);
        }

        base.Seed(context);
    }
}

The code above generates three tables when ran. These being FeedbackCategories, FeedbackCategoryFeedbackItems and FeedbackItems

The table FeedbackCategories is seeded with some already existing categories. The trouble comes when I try to create a new FeedbackItem that has one or more categories.

The Json i provide is the following:

{
"categorys": [
    {
        "$id": "1",
        "Feedbacks": [],
        "Id": 1,
        "Value": "Android"
    }
],
"subscriptionUserName": "name",
"subscriptionUserEmail": "my@email.com",
"content": "this is a feedback item",
"notify": false,
"subscriptionId": 2
}

This is converted into a FeedbackItem and handled by the following code

public class FeedbackSqlRepository : IFeedbackSqlRepository
{

    public int Create(FeedbackItem feedback)
    {
        if (feedback == null)
        {
            throw new ArgumentNullException("feedback", "FeedbackItem cannot be empty.");
        }
        using (var context = new FeedbackContext())
        {
            context.FeedbackItems.Add(feedback);
            return context.SaveChanges();
        }
    }
}

The thing that happens here is that EF creates a new FeedbackItem, a new FeedbackCategory an maps the created feedback item to the newly created feedback category in the FeedbackCategoryFeedbackItems table.

this is not the working i want

I want the following:

Create a new FeedbackItem and reverence an existing FeedbackCategory in the FeedbackCategoryFeedbackItems table. My knowledge of EF is too little to understand what's going wrong here and what to do to get the preferred working.

======== Fixed the issue with the following code inside the Create method from the FeedbackSqlRepository:

 foreach (FeedbackCategory feedbackCategory in feedback.Categories)
            {
                context.Entry(feedbackCategory).State = EntityState.Unchanged;
            }
            context.FeedbackItems.Add(feedback);

            return context.SaveChanges();
MegaWubs
  • 624
  • 1
  • 8
  • 17
  • Your problem probably comes from using different context object to get `FeedbackCategory` and another to save them. Try this http://stackoverflow.com/questions/5693843/entity-framework-multiple-object-contexts – Michał Krzemiński Mar 31 '14 at 12:13
  • Can you show us your mapping between FeedbackCategory, FeedbackItem and FeedbackCategoryFeedbackItems ? Do you map one entity one table ? – cat916 Mar 31 '14 at 12:42
  • EF can't track (child) entities across disconnected contexts. You will need to handle the tracking yourself i.e. on server check whether a child entity already exists, and mark it as EntityState.Unmodified before called 'SaveChanges'. – Mashton Mar 31 '14 at 12:43

1 Answers1

3

Entity Framework will not examine entity contents and determine for you if they are new or added.

DbSet.Add() causes ALL entities in an object graph to be marked as added and to generate inserts when you call SaveChanges().

DbSet.Attach() leaves all entities marked as Unmodified.

If some of your entities are new, some are modified and some are just references then you should use either Add() or Attach() and then manually set entity states where necessary before calling SaveChanges().

DbContext.Entry(entity).State = EntityState.Unmodified
JC Ford
  • 6,946
  • 3
  • 25
  • 34
  • Thanks! This works. Fixed it with the following code: `foreach (FeedbackCategory feedbackCategory in feedback.Categories) { context.Entry(feedbackCategory).State = EntityState.Unchanged; } context.FeedbackItems.Add(feedback); return context.SaveChanges();` – MegaWubs Mar 31 '14 at 12:43
  • Alternatively you could use the Attach() method instead of Add() and just set the state for the single entity that is to be added. Probably not a performance difference, but your code would be a bit clearer imo. – JC Ford Mar 31 '14 at 12:53