【问题标题】:SqliteException: SQLite Error 1: 'foreign key mismatch - "Rental" referencing "Movie"'SqliteException: SQLite 错误 1: '外键不匹配 - "Rental" 引用 "Movie"'
【发布时间】:2021-02-14 05:33:35
【问题描述】:

我收到 SqliteException: SQLite 错误 1: '外键不匹配 - "Rental" 引用 "Movie"'。

 CREATE TABLE Movie (
title VARCHAR(30) NOT NULL,
description VARCHAR(300) NOT NULL);



CREATE TABLE Rental (
user_id TEXT  ,
movie_id INT ,
FOREIGN KEY (user_id) REFERENCES AspNetUsers(Email),
FOREIGN KEY (movie_id) REFERENCES Movie(rowid));

使用以下代码。

        public async Task OnGetAsync(int movie_id, string email)
        {
 
            Rental newRental = new Rental();
            newRental.movie_id=movie_id;
            newRental.user_id=email;
        
            _context.Rental.Add(newRental);
            _context.SaveChanges();

        }

电影表有 SQLlite 自动添加的隐式 rowid

我做错了什么?

【问题讨论】:

    标签: c# entity-framework sqlite


    【解决方案1】:

    有几件事:电影似乎缺少 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 提供一些令人印象深刻的查询和数据检索选项,而无需读取大量记录并将客户端拼凑在一起。

    【讨论】:

      猜你喜欢
      • 2018-10-20
      • 1970-01-01
      • 2021-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多