Правильно редактируйте отношения "многие ко многим" EntityFramework C # - PullRequest
0 голосов
/ 07 мая 2018

Я пытаюсь изменить содержимое сводной таблицы в EntityFramework CodeFirst.

Я использую .NET Framework 4.6.1 с EntityFramework 6.1.3.

У меня есть настройки моих моделей так:

public class Post
{
    /// <summary>
    /// The post's id in the database
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// The the categories the post belongs to
    /// </summary>
    [InverseProperty("Posts")]
    public virtual ICollection<Category> Categories { get; set; }

    /// <summary>
    /// The title of the post
    /// </summary>
    [Required]
    public string Title { get; set; }

    /// <summary>
    /// The content of the post
    /// </summary>
    [Required]
    public string Content { get; set; }

    /// <summary>
    /// The moment the post was made
    /// </summary>
    public DateTime? TimeStamp { get; set; }
}

public class Category
{
    /// <summary>
    /// The id of the category in the database
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// The posts that belong to this category
    /// </summary>
    [InverseProperty("Categories")]
    public virtual ICollection<Post> Posts { get; set; }

    /// <summary>
    /// The name of the category
    /// </summary>
    [Required]
    public string Name { get; set; }
}

Как видите, Post может принадлежать многим Category, а Category может иметь много Post '

.

Моя проблема возникает, когда я пытаюсь обновить сообщение и изменить его Category.

Контроллер:

[HttpPut]
[ResponseType(typeof(void))]
public IHttpActionResult PutPost(int id, Post post)
{
    if (id != post.Id)
    {
        return BadRequest();
    }

    var entry = db.Entry(post);
    entry.State = EntityState.Modified;

    // Created at must not be updated.
    entry.Property(x => x.TimeStamp).IsModified = false;

    // Also attatch categories
    foreach (var category in post.Categories) // For the example, I assume all user input is correct.
    {
        db.Categories.Attatch(category); // This is not working
    }

    try
    {
        db.SaveChanges();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!PostExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return StatusCode(HttpStatusCode.NoContent);
}

Проблема:

Все отлично работает: я могу обновить Title и Content из Post без проблем. Но Post.Categories не обновляется в базе данных.

Я видел, как люди обновляли отношения «многие ко многим», сначала получая сущность из базы данных и используя Include(x => x.Relation), а затем редактируя все внутри этого объекта. Однако для этого требуется загрузить большую часть данных из базы данных, что сразу после этого отменяется. Это кажется плохой практикой для меня.

Вопрос: Как правильно обновить отношение «многие ко многим» в EntityFramework, не загружая ненужные данные из базы данных?

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

Не уверен, что вы получите то, что ищете. В любом случае вам придется загрузить граф сущностей в Entity Framework, чтобы он мог отслеживать изменения.

Как написано в вашем примере, Attach (кстати, с ошибкой) добавит объект только в трекер изменений Entity Framework с состоянием по умолчанию «Без изменений».

Вам необходимо сообщить Entity Framework, что запись была изменена, чтобы EF мог обновить записи.

Если вы ЗНАЕТЕ , что предоставленная категория уже существует (что я и предполагаю, когда вы сказали "предполагая, что все пользовательские данные верны"), тогда вы можете использовать следующее:

foreach (var category in post.Categories)
{
    // This will attach the entity and set the entity state to modified
    // Note this will throw an exception if the underlying entity doesn't
    // exist when the "SaveChanges" method is called.
    context.Entry(category).State = EntityState.Modified;
}

Я предполагаю, что вы реализуете какие-то проверки и противовесы, чтобы гарантировать, что запись существует до вышеприведенного кода, но в случае, если вы не хотите проходить через проблему, вот фрагмент кода, который должен работать для вас. Блок «Еще» может быть добавлен для добавления категории, если она еще не существует.

foreach (var category in post.Categories)
{
    var existingRecord = context.Categories.SingleOrDefault(x => x.ID == category.Id);
    if (existingRecord != null)
    {
        context.Entry(existingRecord).CurrentValues.SetValues(category)
    } 
}
0 голосов
/ 07 мая 2018

Если я хорошо понимаю ваши намерения, вы должны попробовать что-то подобное в вашем DBContext. Он создаст третью таблицу с именем PostsCategories, в которой вы будете иметь отношения между Category и Post.

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Category>()
                       .HasMany(t => t.Posts)
                       .WithMany(t => t.Categories)
                       .Map(m =>
                       {
                           m.ToTable("PostsCategories");
                           m.MapLeftKey("CategoryId");
                           m.MapRightKey("PostId");
                       });
   }

РЕДАКТИРОВАТЬ:

Так попробуйте это: В DbContext:

    public void UpdateManyToMany<TSingle, TMany>(
        TSingle localItem,
        Func<TSingle, ICollection<TMany>> collectionSelector,
        int[] collectionIds)
        where TSingle : class
        where TMany : class
    {
        if (collectionIds == null)
            collectionIds = new int[0];

        DbSet<TMany> manyItemDbSet = Set(typeof(TMany)).Cast<TMany>();
        ICollection<TMany> dbColl = collectionSelector(localItem);
        dbColl.Clear();
        foreach (var keyValue in collectionIds)
        {
            var entity = manyItemDbSet.Find(keyValue);
            dbColl.Add(entity);
        }
    }

тогда в вашем контроллере

db.UpdateManyToMany(post, e => e.Categories, post.MultipleCategories);

где post.MultipleCategories - это идентификатор категорий, поэтому вам, вероятно, необходимо добавить какое-либо поле в модель Post.

[NotMapped]
public int[] MultipleCategories{ get; set; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...