为了测试 IHP,我已将Contoso University ASP.NET Core 教程的部分内容转换为 IHP。
\n本教程中的这一步显示了数据模型图。根据该页面,我在这个问题中重点关注的部分涉及Instructor和 ,OfficeAssignment其中 和 具有一对零或一的关系。
C# 中的模型Instructor是:
public class Instructor\n{\n public int ID { get; set; }\n\n [Required]\n [Display(Name = "Last Name")]\n [StringLength(50)]\n public string LastName { get; set; }\n\n [Required]\n [Column("FirstName")]\n [Display(Name = "First Name")]\n [StringLength(50)]\n public string FirstMidName { get; set; }\n\n [DataType(DataType.Date)]\n [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]\n [Display(Name = "Hire Date")]\n public DateTime HireDate { get; set; }\n\n [Display(Name = "Full Name")]\n public string FullName\n {\n get { return LastName + ", " + FirstMidName; }\n }\n\n public ICollection<Course> Courses { get; set; }\n public OfficeAssignment OfficeAssignment { get; set; }\n}\nRun Code Online (Sandbox Code Playgroud)\n这在 sqlite 中产生了下表:
\nCREATE TABLE IF NOT EXISTS "Instructor" (\n "ID" INTEGER NOT NULL CONSTRAINT "PK_Instructor" PRIMARY KEY AUTOINCREMENT,\n "LastName" TEXT NOT NULL,\n "FirstName" TEXT NOT NULL,\n "HireDate" TEXT NOT NULL\n);\nRun Code Online (Sandbox Code Playgroud)\n所以在 IHP 中,我使用了以下内容:
\nCREATE TABLE instructors (\n id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,\n last_name TEXT NOT NULL,\n first_mid_name TEXT NOT NULL,\n hire_date DATE NOT NULL\n);\nRun Code Online (Sandbox Code Playgroud)\nC# 中的模型OfficeAssignment是:
public class Instructor\n{\n public int ID { get; set; }\n\n [Required]\n [Display(Name = "Last Name")]\n [StringLength(50)]\n public string LastName { get; set; }\n\n [Required]\n [Column("FirstName")]\n [Display(Name = "First Name")]\n [StringLength(50)]\n public string FirstMidName { get; set; }\n\n [DataType(DataType.Date)]\n [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]\n [Display(Name = "Hire Date")]\n public DateTime HireDate { get; set; }\n\n [Display(Name = "Full Name")]\n public string FullName\n {\n get { return LastName + ", " + FirstMidName; }\n }\n\n public ICollection<Course> Courses { get; set; }\n public OfficeAssignment OfficeAssignment { get; set; }\n}\nRun Code Online (Sandbox Code Playgroud)\n这在 sqlite 中产生了下表:
\nCREATE TABLE IF NOT EXISTS "OfficeAssignment" (\n "InstructorID" INTEGER NOT NULL CONSTRAINT "PK_OfficeAssignment" PRIMARY KEY,\n "Location" TEXT NULL,\n CONSTRAINT "FK_OfficeAssignment_Instructor_InstructorID" FOREIGN KEY ("InstructorID") REFERENCES "Instructor" ("ID") ON DELETE CASCADE\n);\nRun Code Online (Sandbox Code Playgroud)\n所以在 IHP 中,我使用了以下内容:
\nCREATE TABLE office_assignments (\n id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,\n instructor_id UUID NOT NULL,\n "location" TEXT NOT NULL\n);\nCREATE INDEX office_assignments_instructor_id_index ON office_assignments (instructor_id);\nALTER TABLE office_assignments ADD CONSTRAINT office_assignments_ref_instructor_id FOREIGN KEY (instructor_id) REFERENCES instructors (id) ON DELETE NO ACTION;\nRun Code Online (Sandbox Code Playgroud)\n请注意,ASP.NET Core 版本的表OfficeAssignment仅具有以下列:
InstructorID\nLocation\nRun Code Online (Sandbox Code Playgroud)\n而 IHP 表有:
\nid\ninstructor_id\nlocation\nRun Code Online (Sandbox Code Playgroud)\n即它有一个id列。我将其留在那里,因为它是由 IHP 架构编辑器默认添加的。
Instructor给出in的生成代码Types.hs:
CREATE TABLE IF NOT EXISTS "Instructor" (\n "ID" INTEGER NOT NULL CONSTRAINT "PK_Instructor" PRIMARY KEY AUTOINCREMENT,\n "LastName" TEXT NOT NULL,\n "FirstName" TEXT NOT NULL,\n "HireDate" TEXT NOT NULL\n);\nRun Code Online (Sandbox Code Playgroud)\nIHP 的解释似乎是这样的:
\n(即该字段officeAssignments是复数。)
但是,根据 ASP.NET Core 教程中的图表,讲师可以有 0 或 1 个办公室任务。(即他们是否有办公室。)
\nEntity Framework Core 似乎从 Instructor 上存在以下导航属性得到了这样的信号:每位讲师最多应有一个办公室分配:
\npublic OfficeAssignment OfficeAssignment { get; set; }\nRun Code Online (Sandbox Code Playgroud)\n更新:这已得到证实。请参阅下面标题为“更新 1”的部分。
\n在 C# 应用程序中,假设我创建一个Instructor,指定一个办公室:
我们在sqlite中看到以下内容:
\nsqlite> SELECT * FROM Instructor; SELECT * FROM OfficeAssignment;\nID LastName FirstName HireDate\n-- ----------- --------- -------------------\n1 Fakhouri Fadi 2002-07-06 00:00:00\n2 Harui Roger 1998-07-01 00:00:00\n3 Kapoor Candace 2001-01-15 00:00:00\n4 Zheng Roger 2004-02-12 00:00:00\n5 Abercrombie Kim 1995-03-11 00:00:00\n10 Curry Haskell 1920-01-01 00:00:00\nInstructorID Location\n------------ ------------\n2 Gowan 27\n3 Thompson 304\n10 Haskell Room\nRun Code Online (Sandbox Code Playgroud)\n现在,在 C# 应用程序中,我们创建一名讲师,但不指定办公室:
\n\n我们在sqlite中看到以下内容:
\nsqlite> SELECT * FROM Instructor; SELECT * FROM OfficeAssignment;\nID LastName FirstName HireDate\n-- ----------- --------- -------------------\n1 Fakhouri Fadi 2002-07-06 00:00:00\n2 Harui Roger 1998-07-01 00:00:00\n3 Kapoor Candace 2001-01-15 00:00:00\n4 Zheng Roger 2004-02-12 00:00:00\n5 Abercrombie Kim 1995-03-11 00:00:00\n10 Curry Haskell 1920-01-01 00:00:00\n11 Church Alonzo 1940-01-01 00:00:00\nInstructorID Location\n------------ ------------\n2 Gowan 27\n3 Thompson 304\n10 Haskell Room\n11\nRun Code Online (Sandbox Code Playgroud)\n有趣的是,如果我编辑讲师并将办公室留空:
\n\n我们在sqlite中看到以下内容:
\nsqlite> SELECT * FROM Instructor; SELECT * FROM OfficeAssignment;\nID LastName FirstName HireDate\n-- ----------- --------- -------------------\n1 Fakhouri Fadi 2002-07-06 00:00:00\n2 Harui Roger 1998-07-01 00:00:00\n3 Kapoor Candace 2001-01-15 00:00:00\n4 Zheng Roger 2004-02-12 00:00:00\n5 Abercrombie Kim 1995-03-11 00:00:00\n10 Curry Haskell 1920-01-01 00:00:00\n11 Church Alonzo 1940-01-01 00:00:00\nInstructorID Location\n------------ ------------\n2 Gowan 27\n3 Thompson 304\nRun Code Online (Sandbox Code Playgroud)\n即被OfficeAssignment删除。
这段代码的实现是:
\nif (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))\n instructorToUpdate.OfficeAssignment = null;\nRun Code Online (Sandbox Code Playgroud)\n这是在 IHP 端进行设置以对 C# 应用程序进行建模的好方法吗?或者我应该在 IHP 方面进行一些更改,以更紧密地模拟这种一对零或一的关系?
\n我浏览了IHP 手册的关系部分,但没有注意到任何有关这种一对零或一关系的内容。只是想确保在我冒险进入表单方面之前我已经正确设置了模型。
\n如果有帮助,包含上述内容的项目存储库位于:
\nhttps://github.com/dharmatech/ContosoUniversityIhp/tree/2021-09-04-02-queryOr-fix
\n(这是非常混乱的,因为它是为了实验。)
\n我意识到这是一个复杂的问题,但我希望它可以作为人们将来在 IHP 中建立类似关系场景的示例。
\nEntity Framework Core 文档包含以下部分:
\n它提到:
\n\n\n一对一关系双方都有参考导航属性。它们遵循与一对多关系相同的约定,但在外键属性上引入了唯一索引,以确保只有一个依赖项与每个主体相关。
\n
这确实是我们在Instructor和 的C# 模型中看到的OfficeAssignment。所以我想问题是,IHP 是否明确支持这种关系?如果没有,在当前机制下模拟它的好方法是什么?
Instructor似乎为了对Instructor他们可以拥有一个或零个办公室这一事实进行建模,生成的模型应该有一个如下所示的字段:
officeAssignment :: Maybe OfficeAssignment\nRun Code Online (Sandbox Code Playgroud)\n如前所述,目前情况如下:
\ndata Instructor\' officeAssignments = Instructor {\n id :: (Id\' "instructors"), \n lastName :: Text, \n firstMidName :: Text, \n hireDate :: Data.Time.Calendar.Day, \n officeAssignments :: officeAssignments, \n meta :: MetaBag\n} deriving (Eq, Show)\nRun Code Online (Sandbox Code Playgroud)\n如果我们看一下office_assignmentsIHP 方面的表格:
CREATE TABLE office_assignments (\n id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,\n instructor_id UUID NOT NULL,\n "location" TEXT NOT NULL\n);\nRun Code Online (Sandbox Code Playgroud)\n很明显,由于有一列,所以对于给定的id,我们可以有任意数量的行。office_assignmentinstructor_id
但是,如果我们看一下 C# 版本:
\nCREATE TABLE IF NOT EXISTS "OfficeAssignment" (\n "InstructorID" INTEGER NOT NULL CONSTRAINT "PK_OfficeAssignment" PRIMARY KEY,\n "Location" TEXT NULL,\n CONSTRAINT "FK_OfficeAssignment_Instructor_InstructorID" FOREIGN KEY ("InstructorID") REFERENCES "Instructor" ("ID") ON DELETE CASCADE\n);\nRun Code Online (Sandbox Code Playgroud)\n我们注意到:
\nid专栏。InstructorID列。InstructorID是个PRIMARY KEYOfficeAssignments因此,这似乎强制了这样一个事实:对于任何给定的只能有一行Instructor。因此,也许就像将 IHP 架构更改为一样简单:
\nCREATE TABLE office_assignments (\n instructor_id UUID PRIMARY KEY NOT NULL,\n "location" TEXT NOT NULL\n);\n\nCREATE INDEX office_assignments_instructor_id_index ON office_assignments (instructor_id);\n\nALTER TABLE office_assignments ADD CONSTRAINT office_assignments_ref_instructor_id FOREIGN KEY (instructor_id) REFERENCES instructors (id) ON DELETE NO ACTION;\nRun Code Online (Sandbox Code Playgroud)\n好的,使用我更新的模式编辑器,office_assignments它现在看起来像这样:
CREATE TABLE office_assignments (\n instructor_id UUID PRIMARY KEY NOT NULL,\n "location" TEXT NOT NULL\n);\nRun Code Online (Sandbox Code Playgroud)\n这是编译期间的结果:
\n[ 4 of 23] Compiling Generated.Types ( build/Generated/Types.hs, interpreted )\n\nbuild/Generated/Types.hs:133:71: error:\n \xe2\x80\xa2 Couldn\'t match type \xe2\x80\x98"instructors"\xe2\x80\x99 with \xe2\x80\x98"office_assignments"\xe2\x80\x99\n arising from a use of \xe2\x80\x98QueryBuilder.filterWhere\xe2\x80\x99\n \xe2\x80\xa2 In the fifth argument of \xe2\x80\x98Instructor\xe2\x80\x99, namely\n \xe2\x80\x98(QueryBuilder.filterWhere\n (#instructorId, id) (QueryBuilder.query @OfficeAssignment))\xe2\x80\x99\n In the expression:\n Instructor\n id lastName firstMidName hireDate\n (QueryBuilder.filterWhere\n (#instructorId, id) (QueryBuilder.query @OfficeAssignment))\n def {originalDatabaseRecord = Just (Data.Dynamic.toDyn theRecord)}\n In an equation for \xe2\x80\x98theRecord\xe2\x80\x99:\n theRecord\n = Instructor\n id lastName firstMidName hireDate\n (QueryBuilder.filterWhere\n (#instructorId, id) (QueryBuilder.query @OfficeAssignment))\n def {originalDatabaseRecord = Just (Data.Dynamic.toDyn theRecord)}\n |\n133 | let theRecord = Instructor id lastName firstMidName hireDate (QueryBuilder.filterWhere (#instructorId, id) (QueryBuilder.query @OfficeAssignment)) def { originalDatabaseRecord = Just (Data.Dynamic.toDyn theRecord) }\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nFailed, three modules loaded.\n\nbuild/Generated/Types.hs:234:20: error:\n \xe2\x80\xa2 Couldn\'t match type \xe2\x80\x98OfficeAssignment\' instructorId0\n -> instructorId0\xe2\x80\x99\n with \xe2\x80\x98Id\' "office_assignments"\xe2\x80\x99\n arising from a use of \xe2\x80\x98QueryBuilder.filterWhere\xe2\x80\x99\n \xe2\x80\xa2 In the second argument of \xe2\x80\x98(|>)\xe2\x80\x99, namely\n \xe2\x80\x98QueryBuilder.filterWhere (#instructorId, instructorId)\xe2\x80\x99\n In the expression:\n builder |> QueryBuilder.filterWhere (#instructorId, instructorId)\n In an equation for \xe2\x80\x98QueryBuilder.filterWhereId\xe2\x80\x99:\n QueryBuilder.filterWhereId instructor_id builder\n = builder |> QueryBuilder.filterWhere (#instructorId, instructorId)\n |\n234 | builder |> QueryBuilder.filterWhere (#instructorId, instructorId)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
149 次 |
| 最近记录: |