Новые записи, вставленные в таблицу внешнего ключа при вставке в родительскую таблицу

asp.net-mvc-4 entity-framework-6

Вопрос

Я новичок в Asp.net MVC и работает над простым блого-приложением (Asp.Net MVC5, EF6) для обучения.

Я использую шаблон репозитория для архитектуры решения с первой миграцией EF кода, Ninject для DI. На стороне клиента я использую jQuery Grid для администратора для управления сообщениями, категориями и тегами.

- Blog.Model: Post.cs, Category.cs, Tags.cs

public class Post
{
    [Required(ErrorMessage = "Id is required")]
    public int Id { get; set; }

    [Required(ErrorMessage = "Title is required")]
    [StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Short description is required")]
    public string ShortDescription { get; set; }

    [Required(ErrorMessage = "Description is required")]
    public string Description { get; set; }

    public bool Published { get; set; }

    [Required(ErrorMessage = "PostedOn date is required")]
    public DateTime PostedOn { get; set; }

    public DateTime? ModifiedOn { get; set; }

    [ForeignKey("Category")]
    public virtual int CategoryId { get; set; }

    public virtual Category Category { get; set; }

    public virtual IList<Tag> Tags { get; set; }
}

public class Category
{
    [Key]
    public int CategoryId { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]     
    public string Description { get; set; }

    [JsonIgnore] 
    public virtual IList<Post> Posts { get; set; }
}

public class Tag
{
    public int Id { get; set; }

    [Required(ErrorMessage = "Name is required")]
    [StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
    public string Name { get; set; }

    public string Description { get; set; }

    [JsonIgnore]
    public IList<Post> Posts { get; set; }

}

- Blog.Repository: BlogRepository, IBlogRepository, BlogContext

public class Post
{
    [Required(ErrorMessage = "Id is required")]
    public int Id { get; set; }

    [Required(ErrorMessage = "Title is required")]
    [StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Short description is required")]
    public string ShortDescription { get; set; }

    [Required(ErrorMessage = "Description is required")]
    public string Description { get; set; }

    public bool Published { get; set; }

    [Required(ErrorMessage = "PostedOn date is required")]
    public DateTime PostedOn { get; set; }

    public DateTime? ModifiedOn { get; set; }

    [ForeignKey("Category")]
    public virtual int CategoryId { get; set; }

    public virtual Category Category { get; set; }

    public virtual IList<Tag> Tags { get; set; }
}

public class Category
{
    [Key]
    public int CategoryId { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]     
    public string Description { get; set; }

    [JsonIgnore] 
    public virtual IList<Post> Posts { get; set; }
}

public class Tag
{
    public int Id { get; set; }

    [Required(ErrorMessage = "Name is required")]
    [StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
    public string Name { get; set; }

    public string Description { get; set; }

    [JsonIgnore]
    public IList<Post> Posts { get; set; }

}

- Blog.Web: AdminController.cs, NinjectWebCommon.cs

AdminController отправляет / потребляет данные в формате Json.

public class Post
{
    [Required(ErrorMessage = "Id is required")]
    public int Id { get; set; }

    [Required(ErrorMessage = "Title is required")]
    [StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Short description is required")]
    public string ShortDescription { get; set; }

    [Required(ErrorMessage = "Description is required")]
    public string Description { get; set; }

    public bool Published { get; set; }

    [Required(ErrorMessage = "PostedOn date is required")]
    public DateTime PostedOn { get; set; }

    public DateTime? ModifiedOn { get; set; }

    [ForeignKey("Category")]
    public virtual int CategoryId { get; set; }

    public virtual Category Category { get; set; }

    public virtual IList<Tag> Tags { get; set; }
}

public class Category
{
    [Key]
    public int CategoryId { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]     
    public string Description { get; set; }

    [JsonIgnore] 
    public virtual IList<Post> Posts { get; set; }
}

public class Tag
{
    public int Id { get; set; }

    [Required(ErrorMessage = "Name is required")]
    [StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
    public string Name { get; set; }

    public string Description { get; set; }

    [JsonIgnore]
    public IList<Post> Posts { get; set; }

}

Я использую пользовательскую привязку модели, потому что я получал исключение проверки при сохранении сообщения, так как список категорий и тегов, полученных из сетки, не сопоставляется с реальными объектами в модели приложения. Поэтому в привязке настраиваемой модели я заполняю объект Post фактическими объектами, полученными из сетки. Этот объект Post отправляется контроллеру, который сохраняет в базу данных с использованием DbContext и репозитория.

public class Post
{
    [Required(ErrorMessage = "Id is required")]
    public int Id { get; set; }

    [Required(ErrorMessage = "Title is required")]
    [StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Short description is required")]
    public string ShortDescription { get; set; }

    [Required(ErrorMessage = "Description is required")]
    public string Description { get; set; }

    public bool Published { get; set; }

    [Required(ErrorMessage = "PostedOn date is required")]
    public DateTime PostedOn { get; set; }

    public DateTime? ModifiedOn { get; set; }

    [ForeignKey("Category")]
    public virtual int CategoryId { get; set; }

    public virtual Category Category { get; set; }

    public virtual IList<Tag> Tags { get; set; }
}

public class Category
{
    [Key]
    public int CategoryId { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Category Name is required")]
    [StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]     
    public string Description { get; set; }

    [JsonIgnore] 
    public virtual IList<Post> Posts { get; set; }
}

public class Tag
{
    public int Id { get; set; }

    [Required(ErrorMessage = "Name is required")]
    [StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
    public string Name { get; set; }

    public string Description { get; set; }

    [JsonIgnore]
    public IList<Post> Posts { get; set; }

}

Проблема. Когда сообщение сохраняется, контекст данных вставляет новые строки для категории и тегов в соответствующие таблицы. Вновь созданная запись относится к новой категории (Id: 22) в колонке Foreign key.

После:

введите описание изображения здесь

Категория:

введите описание изображения здесь

Тег:

введите описание изображения здесь

Я думаю, причина в том, что когда сущность сохраняется, она привязана к другому объекту ObjectContext, и мне нужно прикрепить его к текущему контексту, но не знаете, как это сделать? Я нашел аналогичный вопрос, заданный раньше, но на него нет принятого ответа. Любая помощь будет принята с благодарностью.

Принятый ответ

Я смог решить вышеупомянутую проблему, добавив в категорию category и tags значение objectcontext вручную, что указывает EF на изменения, которые он должен выполнить. Таким образом, он не создает новые записи в родительских таблицах категорий и тегов.

  public int SavePost(Post post)
    {
        //attach tags to db context for Tags to tell EF 
        //that these tags already exist in database
        foreach (var t in post.Tags)
        {
            _db.Tags.Attach(t);
        }

        //tell EF that Category already exists in Category table
        _db.Entry(post.Category).State = EntityState.Modified;

        _db.Posts.Add(post);

        _db.SaveChanges();

        return post.Id;
    }

 public void EditPost(Post post)
    {
        if (post == null) return;

        //get current post from database
        var dbPost = _db.Posts.Include(p => p.Tags).SingleOrDefault(p => p.Id == post.Id);

        //get new list of tags
        var newTags = post.Tags.Select(tag => new Tag() { Id = tag.Id, Name = tag.Name, Description = tag.Description }).ToList();

        if (dbPost != null)
        {
            //get category from its parent table and assign to db post
            dbPost.Category = _db.Categories.Find(post.Category.CategoryId); ;

            //set scalar properties
            _db.Entry(dbPost).CurrentValues.SetValues(post);

            //remove tags from post in database
            foreach (var t in dbPost.Tags.ToList())
            {
                if (!newTags.Contains(t))
                {
                    dbPost.Tags.Remove(t);
                }
            }

            //add tags to post in database
            foreach (var t in newTags)
            {
                if (dbPost.Tags.All(p => p.Id != t.Id))
                {
                    var tagInDb = _db.Tags.Find(t.Id);
                    if (tagInDb != null)
                    {
                        dbPost.Tags.Add(tagInDb);
                    }
                }
            }
        }

        //save changes
        _db.SaveChanges();
    }



Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему