【问题标题】:How do I get last inserted ID(String) from the table如何从表中获取最后插入的 ID(字符串)
【发布时间】:2019-04-21 05:30:02
【问题描述】:

我想使用数据库中最后插入的 ID,但该 ID 是字符串格式。有什么方法可以获取最后插入的字符串 ID?

每次使用

p.UserId = db.AspNetUsers.Max(m => m.Id);

但它返回错误的结果。我认为它是字符串,所以 max 不起作用。谁能建议如何解决这个问题?

表结构:

  • 患者:ID (PK)、UID(UserLogin 的 fk)、姓名

AspNetUsers 表:

CREATE TABLE [dbo].[AspNetUsers] 
(
    [Id]    NVARCHAR(128) NOT NULL,
    [Email] NVARCHAR (256) NULL,
)

患者表:

CREATE TABLE Patient
(
     PatientId INT IDENTITY(1,1) PRIMARY KEY,
     PId AS 'P' + RIGHT('00000' + CAST(PatientId AS NVARCHAR(10)), 10) PERSISTED,
     UserId NVARCHAR(128) FOREIGN KEY REFERENCES AspNetUsers(Id)
)

示例 ID 的格式如下:62d8d072-5261-4aaf-b552-d75453cc3421

这是代码,首先我从 AspNetUsers 表中的视图中获取值,因此它将在表中生成 id,我想将该 Id 用于我的 Patient 表。

[HttpPost]
[ValidateAntiForgeryToken]
[AllowAnonymous]
public async Task<ActionResult> SignUp(Patientview pv)
{
        ClinicDbContext db = new ClinicDbContext();

        if (ModelState.IsValid)
        {
            try
            {
                var user = new ApplicationUser { UserName = pv.Email, Email = pv.Email };
                var result = await UserManager.CreateAsync(user, pv.Password);

                if (result.Succeeded)
                {
                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

                }
                else
                {
                    return View(pv);
                }

                Patient p = new Patient();
                p.FirstName = pv.FirstName;
                p.LastName = pv.LastName;
                p.DateOfBirth = pv.DateOfBirth;
                p.Email = pv.Email;
                p.PhoneNumber = pv.PhoneNumber.ToString();
                p.StreetAddress = pv.StreetAddress.ToString();
                p.City = pv.City;
                p.State = pv.State;
                p.ZipCode = pv.ZipCode;
                p.UserId = db.AspNetUsers.Max(m => m.Id);

                _context.Patients.Add(p);
                _context.SaveChanges();

                return RedirectToAction("Index", "Admin");
            }
            catch (DbEntityValidationException e)
            {
                foreach (var eve in e.EntityValidationErrors)
                {
                    System.Diagnostics.Debug.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                    foreach (var ve in eve.ValidationErrors)
                    {
                        System.Diagnostics.Debug.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                            ve.PropertyName, ve.ErrorMessage);
                    }
                }
                throw;
            }
        }
        else
        {
            return View(pv);
        }
}

【问题讨论】:

标签: sql-server asp.net-mvc entity-framework


【解决方案1】:

通常使用 EF,您会使用 FK 映射实体之间的关系,并在保存主记录和关联记录时让 EF 管理 FK 的分配。但是,在您的情况下,这种关系是相对松散耦合的。

是否所有的患者记录都将与经过 ASP.Net 身份验证的用户相关联?如果是这样,您可以查看更改 Patient 上的 PK 类型以匹配 ASPNet 表,然后将 Patient 设置为与 ASPNetUser 的一对一关系。

或者,如果患者可以是 ASPNetUser(但也可以分离),那么我会考虑将 PatientId 列作为自动递增整数或顺序 GUID (newsequentialId()) 添加到 ASPNetUser 并将其映射到患者中的 PatientId可选或需要多对一。这绕过了 ASPNetUser 上字符串 PK 的丑陋,并允许您使用更适合您的应用程序的 FK 类型。 (整数或 GUID)

您可以使用第三个选项来获取新插入的 PK; PK 只会在 DbContext SaveChanges 之后创建,因此您只需调用两次 SaveChanges 即可获得第一个 id:

//...
var user = new ApplicationUser { UserName = pv.Email, Email = pv.Email };
var result = await UserManager.CreateAsync(user, pv.Password);

if (result.Succeeded)
  await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
else
  return View(pv);

_context.SaveChanges();
Patient p = new Patient();
p.PatientId = Guid.Parse(user.UserId); // User ID will be populated after the above SaveChanges call.                
p.FirstName = pv.FirstName;
//...
_context.SaveChanges(); //Save the Patient.

通常情况下,这并不理想,因为您可能会遇到第一个 SaveChanges 成功但第二个失败的情况。这些记录可能应该一起通过,因此可以通过事务范围或单元或工作(例如 DbContextScope)来实现。

使用事务作用域:

using (var tx = new TransactionScope())
{ 
    var user = new ApplicationUser { UserName = pv.Email, Email = pv.Email };
    var result = await UserManager.CreateAsync(user, pv.Password);

    if (result.Succeeded)
      await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
    else
      return View(pv);

    _context.SaveChanges();
    Patient p = new Patient();
    p.PatientId = Guid.Parse(user.UserId); // User ID will be populated after the above SaveChanges call.                
    p.FirstName = pv.FirstName;
    //...
    _context.SaveChanges(); //Save the Patient.

  tx.Complete()
}

一般来说,映射实体之间的关系会是更好的选择,因为这样可以设置 FK 约束来管理诸如级联删除之类的事情。

【讨论】:

    【解决方案2】:

    我了解到您想获取数据库中最后插入的字段。

    为什么不使用 Last() 函数。 :)

    p.UserId = db.AspNetUsers.Select(X=>x.id).Last(); 
    

    将为您提供表格最后一行的用户 ID。

    Max 正在选择最大值,它可能会或可能不会产生最后一个值。

    【讨论】:

    • 永远不要相信选择结果的顺序,除非指定了 order by。最后插入的可能会在最后,运气在你身边,但不能保证。
    • 在多用户系统上也是个坏主意。值得一读wiki.lessthandot.com/index.php/…
    • @RemcoteWierik 非常真实,让我意识到订购的重要性。谢谢:)
    • @AbhilashGopalakrishna 还值得一提的是,使用 LastOrDefault 会更安全,因为 AspNetUsers 表可能为空,这会导致 InvalidOperationException。
    • AFAIK Last/LastOrDefault 不能使用 /w EF IQueryable。您必须 OrderByDescending 并获得 FirstOrDefault。这是不可取的,因为并发写入很容易看到异常,即尝试读取“最新”ID 会获取由不同会话插入的全新记录。通常不会进行测试,但会在标称到重负载的情况下发生。
    猜你喜欢
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 2019-05-28
    • 1970-01-01
    相关资源
    最近更新 更多