有几件事:电影似乎缺少 PK。使用身份(自动生成)密钥时,还需要告知 EF。它可以通过命名约定推断出一些,但我建议明确以避免意外。需要为适当的关系设置您的实体:
即
public class Movie
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int movie_id { get; set; }
// ...
}
public class Rental
{
[Key, Column(Order=0)]
public int movie_id { get; set; }
[Key, Column(Order=1)]
public int user_id { get; set; }
// ...
}
不幸的是,这并没有真正告诉 EF Movie 和 Rental 之间存在关系。 (或用户和租赁)从应用程序的角度来看,没有这种关系,
有什么保证您的电话从客户那里收到的 Movie_id 存在?这个方法被列为“OnGet”类型的方法也有点奇怪,这意味着一个 GET 操作而不是一个 POST 或 PUT 操作。
通常使用 EF,您将希望为您的域利用导航属性,而不仅仅是公开 FK。此外,您将其定义为异步方法,无需等待任何异步操作。
除非确实有必要,否则我建议避免使用复合键,因此请给 Rental 一个租借 ID,并仅依赖于电影和用户引用的多对一关系:
public class Rental
{
[Key, DatabaseGenerate(DatabaseGeneratedOption.Identity)]
public int rental_id { get; set; }
// ....
public int movie_id { get; set; }
[ForeignKey("movie_id")]
public virtual Movie Movie { get; set; }
public int user_id { get; set; }
[ForeignKey("user_id")]
public virtual User User { get; set; }
}
public void RentMovie(int movie_id, string email)
{
var movie = _context.Movies.Single(x => x.Movie_Id == movie_id);
// Better would be to go to the Session for the current logged in user rather than trusting what is coming from the client...
var user = _context.Users.Single(x => x.Email = email);
try
{
Rental newRental = new Rental
{
Movie = movie;
User = user;
};
_context.Rental.Add(newRental);
_context.SaveChanges();
}
catch
{ // TODO: Handle what to do if a movie could not be rented.
}
}
在上面的示例中,我们尝试加载请求的电影和用户。如果这些不存在,这将落入全局异常处理程序,该处理程序应设置为结束当前登录会话并记录异常。 (即应捕获任何篡改或无效状态并将用户注销。)在尝试记录租赁时,可以设置异常处理以向用户显示消息等,而不是硬故障。
您可以更进一步,从实体中删除 FK 属性,并使用 Shadow Properties (EF Core) 或 .Map(x => x.MapKey()) (EF6) 来设置关系。这样可以避免使用两个事实来源来查看/更新实体之间的关系。
Movie 对象可以有一个ICollection<Rental> 可选,其中Rentals 可以有一个RentedDate 和ReturnedDate,例如,这样可以检查电影以查看是否有副本可供租用。 IE。按名称搜索,然后确定当前是否有一份或多份副本。可以将 Rental 记录添加到 movie.Rentals,而不是将 Rentals 视为顶级实体。
使用导航属性是 EF 的一项强大功能,可以通过 Linq 提供一些令人印象深刻的查询和数据检索选项,而无需读取大量记录并将客户端拼凑在一起。