我有一个Books类,其中ApplicationUser为作者。在我的ApplicationUser类中,有一个用户已编写的“书籍”列表(一对多)。
Books类还具有一个ApplicationUsers列表,用于记录喜欢的数量。但是,单个ApplicationUser只能喜欢一本书。当用户喜欢另一本书时,类似的东西会从以前的所有书籍中删除。
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Book> Likes { get; set; } //NEW
}
public class Book
{
public string Id { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; } //Author
.......
public virtual ICollection<ApplicationUser> Likes { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Book>()
.HasRequired<ApplicationUser>(s => s.ApplicationUser)
.WithMany(s => s.Books)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Book>() //NEW
.HasMany<ApplicationUser>(s => s.Likes)
.WithMany(s => s.Likes);
}
关系一定有问题。单个用户只能喜欢一本书。但是用户应该可以喜欢很多书。
在检查用户在该书的“喜欢”中是否存在用户之后,book.Likes.Add(user);
会简单地添加一个喜欢。
但是Book与创建Book的ApplicationUser之间存在一对多关系。但是,不需要ApplicationUser和Likes之间的关系。
[ResponseType(typeof(void))]
public IHttpActionResult Like(string bookid, bool action)
{
Book book = db.Books.Find(bookid);
var user = UserManager.FindById(User.Identity.GetUserId());
if (book == null || user == null)
return BadRequest();
if (action)
{
if (!book.Likes.Contains(user))
book.Likes.Add(user);
}
else
{
if (book.Likes.Contains(user))
book.Likes.Remove(user);
}
//db.Entry(book).State = EntityState.Modified;
try {
db.SaveChanges();
}
catch (Exception e) {
throw;
}
return StatusCode(HttpStatusCode.NoContent);
}
更新:因此,按照@Alex Kudryashev的建议,我将Likes集合添加到ApplicationUser类。但是我现在在迁移和更新数据库时遇到此错误。
在表'BookApplicationUsers'上引入FOREIGN KEY约束'FK_dbo.BookApplicationUsers_dbo.AspNetUsers_ApplicationUser_Id'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。 无法创建约束或索引。查看以前的错误。
答案 0 :(得分:0)
导致问题的原因在于您对ApplicationUsers
和Books
之间的关系不了解。
每个ApplicationUser
都创作了零个或多个Books
,每个Book
被零个或多个ApplicationUsers
喜欢。因此ApplicationUsers
和Books
之间的关系是多对多的。
首先在EF代码中编写如下:
class ApplicationUser
{
public int Id {get; set;}
// every ApplicationUser has Authored zero or more books (many-to-many)
public virtual ICollection<Book> AuthoredBooks {get; set;}
... // other properties
}
class Book
{
public int Id {get; set;}
// every Book has been Liked by zero or more ApplicationUsers (many-to-many)
public virtual ICollection<ApplicationUsers> Likers {get; set;}
... // other properties
}
class MyDbContext : DbContext
{
public DbSet<ApplicationUser> ApplicationUsers {get; set;}
public DbSet<Book> Books {get; set;}
}
请参见Configure many-to-many relationship
在MyDbContext.OnModelCreating
中:
modelBuilder.Entity<ApplicationUser>()
// every applicationUser has zero or more AuthoredBooks
.HasMany(applicationUser => applicationUser.AuthoredBooks)
// where every Book has zero or more Likers:
.WithMany(book => book.Likers)
// we put the many-to-many in the following junction table:
.Map(junctionTable=>
{
junctionTable.MapLeftKey("ApplicationUserId");
junctionTable.MapRightKey("BookId");
junctionTable.ToTable("ApplicationUsersBooks");
});
尽管创建了联结表,但与多对多表一样,您不会在dbContext
中提及它。每当您使用ICollection时,实体框架都会知道GroupJoin需要连接表。
获取所有撰写过真正旧书的年轻ApplicationUsers:
var result = myDbContext.ApplicationUsers
.Where(user => user.BirtDate > new DateTime(2010, 1, 1)
.Select(user => new
{
Id = user.Id,
Name = user.Name,
AuthoredOldBooks = user.Books
.Where(book => book.PublicationDate < new DateTime(1900, 1, 1)
.ToList(),
}
或者获取一个或多个女性喜欢的所有软件书籍:
var results = myDbContext.Books
.Where(book => book.Genre == BookGenre.Software
&& book.Likers.Where(liker => liker.Gender == Gender.Female).Any());