0

I have a simple Many to Many. Categories on Products. Each has a virtual List of the other. I create a product like this:

product = new Product()
{
    AccountId = this.AccountId,
    Name = this.ProductName,
    Price = this.ProductPrice,
    Slug = this.ProductSlug,
    Sku = this.ProductSku,
    Favorite = true,
    Categories = new List<Category>()
                {
                    category 
                }
};

product.Id = productManager.Save(product);

Then, when I go to save it, it won't save the many to many. I've googled and tried quite a few different variations of this code, and still nothing:

public new String Save(Product data)
{
    try
    {
        var entity = GetById(data.Id);

        if (entity == null)
        {
            this.Context.Set<Product>().Add(data);
        }
        else
        {
            this.Context.Entry(entity).CurrentValues.SetValues(data);
        }

        data.Id = this.Context.SaveChanges();

        var product = Context.Products
            .Include("Categories")
            .FirstOrDefault(t => t.Id == data.Id);

        /**
            * Categories
            */
        var categories = this.Context.Categories
            .Where(t => t.AccountId == data.AccountId);

        var productCategories = new List<Category>();

        if (data.SelectedCategoryIds != null && data.SelectedCategoryIds.Any())
        {
            productCategories.AddRange(categories
                .Where(category => data.SelectedCategoryIds.Contains(category.Id)));
        }

        product.Categories.Clear();
        product.Categories.AddRange(productCategories);

        this.Context.SaveChanges();

        return data.Id;
    }
    catch (Exception ex)
    {
        Log.Error(ex);

        return null;
    }
}

What am I missing here? Why will it not persist my many to many Categories?

gunr2171
  • 16,104
  • 25
  • 61
  • 88
  • Do you have any other details besides it wont save? Does it throw an exception? Does anything hit the DB? Also it seems weird that you're setting the product's Id to the result of context.SaveChanges(). That method returns the number of objects written to the DB, not an Id. – Neil Smith Jul 03 '14 at 18:37
  • Why? Let me expand on that. Why??? Seriously, though, why have you created a `Save` method that does all this? You're basically recreating `SaveChanges` in Entity Framework, and not doing as good a job. – Chris Pratt Jul 03 '14 at 18:42
  • @Smith.h.Neil - There is no exception. The product saves fine. The categories are the only thing that don't persist. –  Jul 03 '14 at 18:45
  • Like @ChrisPratt is saying, have you tried to just attach the product and then call context.SaveChanges() instead of using your save method? – Neil Smith Jul 03 '14 at 18:46
  • @Chris - I can't just call SaveChanges. The data isn't attached when I pass it back from the view bc I dont lkeep the context open. Also, The whole first part is actually in a generic save method that I didn't show here but is the same code. –  Jul 03 '14 at 18:47
  • I am calling the save changes after attaching the object. The categories don't get saved there –  Jul 03 '14 at 18:47
  • possible duplicate of [Insert/Update Many to Many Entity Framework . How do I do it?](http://stackoverflow.com/questions/4253165/insert-update-many-to-many-entity-framework-how-do-i-do-it) – Gert Arnold Jul 03 '14 at 20:28

1 Answers1

0

This isn't technically an answer, but it's too long for a comment, and it needs to be said. It looks like you're trying to abstract some of the Entity Framework stuff, but this is the absolutely wrong way to go about it. In one of my projects, for example, I have a service that abstracts away Entity Framework from the rest of my application and here's some relevant code from that to demonstrate the difference:

public virtual void Update<TEntity>(TEntity entity)
    where TEntity : class
{
    context.Set<TEntity>().Attach(entity);
    context.Entry(entity).State = EntityState.Modified;
}

public virtual void Save()
{
    try
    {
        context.SaveChanges();
    }
    catch (DbEntityValidationException ex)
    {
        var errorMessages = ex.EntityValidationErrors
            .SelectMany(x => x.ValidationErrors)
            .Select(x => x.ErrorMessage);
        var fullErrorMessage = string.Join("; ", errorMessages);
        var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
        throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
    }
}

In my application, then, this is all that happens:

var entity = new Something { ... };
service.Update<Something>(entity);
service.Save();

So, there's quite a bit going on in the code above, so let me step back for a minute. First, my service utilizes generic methods so I can work with any entity without having to write new methods or service classes. The <TEntity> stuff is what makes this work, and isn't strictly required. Also, when I later do Update<Something>, I'm utilizing this generic method, so specifying the class I'm working with, in this case Something. The only part you need to pay attention to is these two lines of my Update<> method, which I'm going to modify to fit your situation:

context.Set<Product>().Attach(product);
context.Entry(product).State = EntityState.Modified;

Then, the only other thing that's necessary to save the changes (from my Save method):

context.SaveChanges();

That's it. If that code alone is not enough to update your entity, then frankly, you're doing something wrong, and you need to unwind your code and figure out what that is.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444