C# CUD存储过程:支架式Insert存储过程中两个SELECT语句的用途是什么?
通过Tom Dykstra的教程,介绍了如何设置EF6以使用CUD的存储过程 当通过package manager控制台添加C# CUD存储过程:支架式Insert存储过程中两个SELECT语句的用途是什么?,c#,sql-server,stored-procedures,entity-framework-6,entity-framework-migrations,C#,Sql Server,Stored Procedures,Entity Framework 6,Entity Framework Migrations,通过Tom Dykstra的教程,介绍了如何设置EF6以使用CUD的存储过程 当通过package manager控制台添加DepartmentSP迁移时,将自动生成以下CreateStoredProcedure()调用以创建Department\u Insert存储过程: CreateStoredProcedure( "dbo.Department_Insert", p => new { Name = p.String(maxLen
DepartmentSP
迁移时,将自动生成以下CreateStoredProcedure()调用以创建Department\u Insert存储过程:
CreateStoredProcedure(
"dbo.Department_Insert",
p => new
{
Name = p.String(maxLength: 50),
Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"),
StartDate = p.DateTime(),
InstructorID = p.Int(),
},
body:
@"INSERT [dbo].[Department]([Name], [Budget], [StartDate], [InstructorID])
VALUES (@Name, @Budget, @StartDate, @InstructorID)
DECLARE @DepartmentID int
SELECT @DepartmentID = [DepartmentID]
FROM [dbo].[Department]
WHERE @@ROWCOUNT > 0 AND [DepartmentID] = scope_identity()
SELECT t0.[DepartmentID]
FROM [dbo].[Department] AS t0
WHERE @@ROWCOUNT > 0 AND t0.[DepartmentID] = @DepartmentID"
);
为什么在自动生成的存储过程中有两个SELECT
语句
我测试了以下简化:
CreateStoredProcedure(
"dbo.Department_Insert",
p => new
{
Name = p.String(maxLength: 50),
Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"),
StartDate = p.DateTime(),
InstructorID = p.Int(),
},
body:
@"INSERT [dbo].[Department]([Name], [Budget], [StartDate], [InstructorID])
VALUES (@Name, @Budget, @StartDate, @InstructorID)
SELECT t0.[DepartmentID]
FROM [dbo].[Department] AS t0
WHERE @@ROWCOUNT > 0 AND t0.[DepartmentID] = scope_identity()"
);
。。这看起来很好,但我可能遗漏了什么
我读过书,也读过书。另外,我查看了EF6 git提交历史记录,发现这是在迁移中启用存储过程支架的第一部分。不幸的是,实体框架经常生成看起来不必要复杂的代码。它似乎更喜欢将查询分解成更多、更小的语句,而不是将其全部处理在一个语句中。同时,请记住,它的代码并非真正设计为“人类可读”,而手写的t-sql通常是这样。这个问题在这个问题上有一些很好的答案:
我想我已经明白了 生成插入存储过程主体的代码可以在中的DmlFunctionSqlGenerator.GenerateInsert()方法中找到 以下是相关代码:
// Part 1
sql.Append(
DmlSqlGenerator.GenerateInsertSql(
firstCommandTree,
_sqlGenerator,
out _,
generateReturningSql: false,
createParameters: false));
sql.AppendLine();
var firstTable
= (EntityType)((DbScanExpression)firstCommandTree.Target.Expression).Target.ElementType;
// Part 2
sql.Append(IntroduceRequiredLocalVariables(firstTable, firstCommandTree));
// Part 3
foreach (var commandTree in commandTrees.Skip(1))
{
sql.Append(
DmlSqlGenerator.GenerateInsertSql(
commandTree,
_sqlGenerator,
out _,
generateReturningSql: false,
createParameters: false));
sql.AppendLine();
}
var returningCommandTrees
= commandTrees
.Where(ct => ct.Returning != null)
.ToList();
// Part 4
if (returningCommandTrees.Any())
{
//...
第1部分生成INSERT
语句。第2部分生成DECLARE
行和第一个SELECT
语句。第4部分生成第二条SELECT
语句
在Contoso University示例中,Department实体类是一个简单的模型类。在这种情况下,传递给DmlFunctionSqlGenerator.GenerateInsert()的commandTrees
集合似乎只包含一个DbInsertCommandTree
元素。因此,第3部分中的foreach
循环被有效地跳过
在其他情况下,commandTrees
集合中可能有多个DbInsertCommandTree
元素,例如当一个实体类扩展另一个实体类并使用时。例如:
[Table("SpecialOrder")]
public class SpecialOrder
{
public int SpecialOrderId { get; set; }
public DateTime Date { get; set; }
public int Status { get; set; }
}
[Table("ExtraSpecialOrder")]
public class ExtraSpecialOrder : SpecialOrder
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ExtraSpecialOrderId { get; set; }
public string ExtraNotes { get; set; }
}
ExtraSpecialLorder实体的脚手架插入存储过程为:
CreateStoredProcedure(
"dbo.ExtraSpecialOrder_Insert",
p => new
{
Date = p.DateTime(),
Status = p.Int(),
ExtraNotes = p.String(),
},
body:
@"INSERT [dbo].[SpecialOrder]([Date], [Status])
VALUES (@Date, @Status)
DECLARE @SpecialOrderId int
SELECT @SpecialOrderId = [SpecialOrderId]
FROM [dbo].[SpecialOrder]
WHERE @@ROWCOUNT > 0 AND [SpecialOrderId] = scope_identity()
INSERT [dbo].[ExtraSpecialOrder]([SpecialOrderId], [ExtraNotes])
VALUES (@SpecialOrderId, @ExtraNotes)
SELECT t0.[SpecialOrderId], t1.[ExtraSpecialOrderId]
FROM [dbo].[SpecialOrder] AS t0
JOIN [dbo].[ExtraSpecialOrder] AS t1 ON t1.[SpecialOrderId] = t0.[SpecialOrderId]
WHERE @@ROWCOUNT > 0 AND t0.[SpecialOrderId] = @SpecialOrderId"
);
请注意,在这种情况下需要两个INSERT
语句
因此,Department实体类的支架式Insert存储过程包含两个SELECT
语句,因为这样,SQL生成可以扩展到生成多个Insert
语句的情况。虽然输出不适用于只有一条INSERT
语句的情况,但是可以手动编辑生成的存储过程主体,以便只有一条SELECT
语句