2

我认为在很多情况下这是一个普遍的问题,但我还没有在网上找到正确的答案。在我的MVC应用程序,我有以下表格:如何在创建新记录之前获取最大ID值?

问题表:

--------------------------------------------- 
IssueID (pk) | CategoryID (fk) | IssueNumber 
--------------------------------------------- 
    1   |  1   |  1 
    2   |  1   |  2 
    3   |  2   |  1 
    4   |  1   |  3 


分类表:

----------------------------------- 
CategoryID (pk) | Prefix | Name 
----------------------------------- 
    1   | COM | Computer 
    2   | GEN | General 


在这里我用IssueID(主键)两个类别(COM和GEN)的字段。另一方面,我想依次生成IssueNumber(1,2,3 ...),而没有任何CategoryID的间隙。另一方面,我也想从上面创建的另一个类别创建IssueNumber。所以问题是:

1)有没有关于上述方法的问题?如果是这样,你对此有什么建议?

2)在创建记录之前,我应该如何获取IssueNumber的最大值?我想在SaveChanges()方法(我使用实体框架)上做到这一点。由于我不会将IssueNumber字段设置为PK,因此我无法使MsSQL Server自动递增此列值。

任何帮助,将不胜感激。提前致谢。


更新:

这是通过使用lambda解决方案:

public int SaveIssue(Issue issue) 
{ 
    int max = context.Issues.Where(m => m.CategoryID == issue.CategoryID).Max(m => m.IssueNumber); 
    issue.IssueNum = max + 1; 

    context.Issues.Add(issue); 
    context.SaveChanges(); 
    return issue.IssueNum ; 
} 
+0

但我没有将IssueNumber字段设置为PK,我想为类别创建最大值+1。所以,在创建一个新的ID之前应该记住CategoryID字段。你能解释如何做到这一点? –

+1

@DavidW问题不在于如何自动生成密钥,而是询问如何自动生成'IssueNumber',它遵循'RowNumber()over(由CategoryID分区,按IssueID排序)'模式。 –

+0

@ScottChamberlain绝对正确,斯科特。我误解了这个问题。谢谢。 –

回答

5

我不知道如何在EF做到这一点,但通过SQL可以做到这一点通过以下(我假设IssueID是一个标识列)

CREATE PROCEDURE usp_CreateNewIssue 
    @catagoryId int, 
    @issueId int OUTPUT, 
    @issueNumber int OUTPUT 
AS 
BEGIN 
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
    BEGIN TRANSACTION 
     declare @maxId int   
     declare @newRow table (IssueID int NOT NULL, IssueNumber int NOT NULL) 

     select @maxId = MAX(IssueNumber) from Issue where CategoryID = @catagoryId 

     Insert Into Issue (CategoryID, IssueNumber) values (@catagoryId, @maxId + 1) 
      OUTPUT INSERTED.IssueID, INSERTED.IssueNumber 
      INTO @newRow 

     select @issueId = IssueID, @issueNumber = IssueNumber from @newRow 
    COMMIT TRANSACTION 
END 

(代码写在浏览器和未经测试的语法错误)

使用SET TRANSACTION ISOLATION LEVEL SERIALIZABLE确保您阅读MAX(IssueNumber)后无其他查询可以修改结果,直到您提交事务之后。

注意:要获得良好性能,请确保CategoryID已在Issue上编入索引,以便SQL引擎可以对其执行Key-Range lock而不是表锁。

+0

我认为FluentAPI可能允许一种机制通过MapToStoredProcedures声明关闭DBModelBuilder对象,这可以通过OnModelCreating事件的重写来访问此类过程... –

+0

@Scott Chamberlain:非常感谢您的回复。为了简化,我使用lambda表达式解决了问题,如更新后的代码所示。投票+ –