2014-01-10 149 views
1

我是新來的Asp.net MVC和工作在一個簡單的博客應用程序(Asp.Net MVC5,EF6)學習。插入父表時插入在外鍵表中的新記錄

我在解決方案體系結構中使用存儲庫模式,並使用EF代碼首次遷移Ninject for DI。在客戶端,我使用jQuery Grid for Admin來管理帖子,類別和標籤。

- 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 interface IBlogRepository 
{ 
    int SavePost(Post post); 

    //Other methods... 
} 

public class BlogRepository : BlogContext, IBlogRepository 
{ 
    public BlogContext _db; 

    public BlogRepository(BlogContext db) 
    { 
     _db = db; 
    } 

    public int SavePost(Post post) 
    { 
     _db.Posts.Add(post); 
     _db.SaveChanges(); 
     return post.Id; 
    } 

    //Other implementations... 
} 

public class BlogContext : DbContext, IDisposedTracker 
{ 
    public BlogContext() : base("BlogDbConnection") { } 

    public DbSet<Post> Posts { get; set; } 
    public DbSet<Tag> Tags { get; set; } 
    public DbSet<Category> Categories { get; set; } 

    public bool IsDisposed { get; set; } 

    protected override void Dispose(bool disposing) 
    { 
     IsDisposed = true; 
     base.Dispose(disposing); 
    } 

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

AdminController以Json格式發送/使用數據。

public class AdminController : Controller 
{  
    private readonly IBlogRepository _blogRepository; 

    public AdminController(IBlogRepository blogRepository) 
    { 
     _blogRepository = blogRepository; 
    } 

    //POST: /Admin/CreatePost 
    [HttpPost, ValidateInput(false)] 
    public ContentResult CreatePost([ModelBinder(typeof(PostModelBinder))] Post model) 
    { 
     string json; 

     ModelState.Clear(); 

     if (TryValidateModel(model)) 
     { 
      var id = _blogRepository.SavePost(model); 
      json = JsonConvert.SerializeObject(
       new 
       { 
        id = id, 
        success = true, 
        message = "Post saved successfully." 
       }); 
     } 
     else 
     { 
      json = JsonConvert.SerializeObject(
       new 
       { 
        id = 0, 
        success = false, 
        message = "Post not saved." 
       }); 
     } 
     return Content(json, "application/json"); 
    } 
} 

public static class NinjectWebCommon 
{ 
    private static void RegisterServices(IKernel kernel) 
    { 
     kernel.Bind<BlogContext>().ToSelf(); //This isn't helping either 
     kernel.Bind<IBlogRepository>().To<BlogRepository>(); 
    } 
} 

我使用自定義模型綁定,因爲我得到驗證異常,而郵政儲蓄自分類的列表,並從電網獲得的標籤不映射到應用程序中的模型實際的對象。因此,在自定義模型綁定中,我使用從網格接收的實際對象填充Post對象。這個Post對象被髮送給使用DbContext和Repository保存到數據庫的控制器。

public class PostModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var post = (Post)base.BindModel(controllerContext, bindingContext); 

     **var blogRepository = new BlogRepository(new BlogContext());**//I think here I need to inject the dependency for BlogContext, but don't know how to do that. 

     if (post.Category != null) 
     { 
      post.Category = blogRepository._db.Categories.AsNoTracking().Single(c => c.CategoryId == post.Category.CategoryId); 
     } 

     var tags = bindingContext.ValueProvider.GetValue("Tags").AttemptedValue.Split(','); 

     if (tags.Length > 0) 
     { 
      post.Tags = new List<Tag>(); 

      foreach (var tag in tags) 
      { 
       var id = int.Parse(tag.Trim()); 
       post.Tags.Add(blogRepository._db.Tags.AsNoTracking().Single(t => t.Id == id)); 
      } 
     } 

     if (bindingContext.ValueProvider.GetValue("oper").AttemptedValue.Equals("edit")) 
      post.ModifiedOn = DateTime.UtcNow; 
     else 
      post.PostedOn = DateTime.UtcNow; 

     return post; 
    } 
} 

問題:當後保存,數據上下文插入其各自的表中分類和標籤的新行。新創建的帖子引用了外鍵列下的新類別(Id:22)。

帖子:

enter image description here

類別:

enter image description here

標籤:

enter image description here

我想這樣做的原因是,當恩tity被保存,它被附加到一個不同的ObjectContext,我需要將它附加到當前的上下文,但不知道如何?我發現類似的question asked before,但沒有一個公認的答案。任何幫助將不勝感激。

回答

2

我能夠通過手動將類別和標記值附加到objectcontext來解決上述問題,這表明EF需要進行更改。這樣它就不會在Category和Tag的父表中創建新的條目。

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(); 
    }