상위 테이블에 삽입 할 때 외래 키 테이블에 삽입 된 새 레코드
-
21-12-2019 - |
문제
나는 ASP.NET MVC의 새로운 기능을하고, 학습을 위해 간단한 블로그 응용 프로그램 (ASP.NET MVC5, EF6)을 연구합니다.
EF 코드 첫 번째 마이그레이션이있는 솔루션 아키텍처의 저장소 패턴을 사용하고 있습니다. DI를위한 NINJECT. 클라이언트 측면에서 jQuery 그리드를 사용하여 게시물, 범주 및 태그를 관리합니다.
- 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>();
}
}
.
INDS에서받은 카테고리 목록 및 그리드에서받은 태그 목록이 응용 프로그램 모델의 실제 객체에 맵핑되지 않기 때문에 유효성 검사 예외를 사용하면 사용자 정의 모델 바인딩을 사용하고 있습니다. 따라서 사용자 정의 모델 바인딩에서는 그리드에서 수신 한 실제 객체가있는 POST 객체를 채우고 있습니다. 이 게시물 개체는 DBContext 및 저장소를 사용하여 데이터베이스에 저장하는 컨트롤러로 전송됩니다.
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)를 나타냅니다.
포스트 :
카테고리 :
태그 :
이유는 엔티티가 저장 될 때 다른 objectContext에 연결되어 있고 현재 컨텍스트에 연결해야하지만 방법을 모르겠습니다. 비슷한 질문은 전에 물어 봤지만 그 답변은 받아 들여지지 않았습니다. 어떤 도움이 크게 감사 할 것입니다.
해결책
카테고리와 태그 값을 objectContext에 첨부하여 위의 문제를 수동으로 해결할 수있었습니다. 이는 변경 해야하는 변경 사항을 나타냅니다.이렇게하면 카테고리 및 태그의 상위 테이블에 새 항목이 생성되지 않습니다.
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();
}
.