如何自定义Identity Models以重命名列并在IdentityServer4中调整数据类型的大小

时间:2018-05-17 11:07:28

标签: c# entity-framework identityserver4

我一直在玩this quickstart example&一直试图看我能在多大程度上自定义数据库(我有一个现有的数据库,我已经尝试复制了一半)。

我尝试重命名字段并重新定义数据类型但遇到以下错误:

  

InvalidOperationException异常:   ' Role.NormalisedName'和' Role.NormalizedName'都被映射到列' NormalisedName'在'角色'但配置为使用不同的数据类型(' nvarchar(max)'和' nvarchar(256)')。

定制IdentityRole的实体目前看起来像这样:

public class Role : IdentityRole<int>
{
    public int RoleId
    {
        get => base.Id;
        set => base.Id = value;
    }
    public string NormalisedName
    {
        get => base.NormalizedName;
        set => base.NormalizedName = value;
    }

    public ICollection<RoleClaim> ClaimsCollection { get; set; }
}

ApplicationDbContext中的覆盖如下所示:

// "Microsoft.AspNetCore.Identity.IdentityRole"
ModelBuilder.Entity<Role>(E =>
{
    E.Property(P => P.Id)
        .HasColumnName("RoleId")
        .ValueGeneratedOnAdd();

    E.Property(P => P.ConcurrencyStamp)
        .HasMaxLength(512)
        .IsConcurrencyToken();

    E.Property(P => P.Name)
    .HasMaxLength(128);

    E.Property(P => P.NormalizedName)
        .HasMaxLength(128)
        .HasColumnName("NormalisedName");

    E.HasKey(P => P.Id);

    E.HasIndex(P => P.NormalizedName)
        .IsUnique()
        .HasName("IX_Roles_NormalisedName");

    E.ToTable("Roles");
});

&#34;角色&#34;数据库中的表格nvarchar(512)Name列的宽度均为NormalisedName;这是由NormalisedName上的索引引起的,该索引将警告非聚簇索引中的密钥长度超过1700字节。生成的警告将如下所示:

  

警告!非聚簇索引的最大密钥长度为1700字节。索引&#39; IX_Roles_NormalisedName&#39;最大长度为2048字节。对于某些大值组合,插入/更新操作将失败。

有没有人知道如何(或者甚至)我可以对此字段施加此限制?仅指定HasMaxLength()扩展方法似乎并不像我预期的那样工作。

1 个答案:

答案 0 :(得分:0)

我看到错误的原因是由于我尝试重新定义列,而不是替换字段并提供了我希望如何将数据存储在SqlServer中的新定义。

因为我对IdentityDbContext中的现有内容进行了重叠编辑,ModelBuilder可以看到两个字段,并试图将所有内容整合在一起。

从Identity对象重命名和删除列的更好示例是IdentityUser,因此我将使用它作为示例,因为它有更多我想要更改的内容。

对象的默认布局如下所示:

public class IdentityUser<TKey> where TKey : IEquatable<TKey>
{
    public IdentityUser();
    public IdentityUser(string userName);
    public virtual DateTimeOffset? LockoutEnd { get; set; }
    public virtual bool TwoFactorEnabled { get; set; }
    public virtual bool PhoneNumberConfirmed { get; set; }
    public virtual string PhoneNumber { get; set; }
    public virtual string ConcurrencyStamp { get; set; }
    public virtual string SecurityStamp { get; set; }
    public virtual string PasswordHash { get; set; }
    public virtual bool EmailConfirmed { get; set; }
    public virtual string NormalizedEmail { get; set; }
    public virtual string Email { get; set; }
    public virtual string NormalizedUserName { get; set; }
    public virtual string UserName { get; set; }
    public virtual TKey Id { get; set; }
    public virtual bool LockoutEnabled { get; set; }
    public virtual int AccessFailedCount { get; set; }

    public override string ToString();
}

假设我们正在使用数据库优先,我想进行各种更改,例如......

  • 使密钥Id成为整数(并且该列在SqlServer中具有'identity'属性)
  • 重命名表格
  • 删除LockoutEnd字段
  • 删除PhoneNumberConfirmed字段
  • 删除PhoneNumber字段
  • 删除LockoutEnabled字段
  • 删除AccessFailedCount字段
  • 并重命名IdNormalizedEmailNormalizedUserName字段

在理论上足够简单,并且在实践中为您提供确切知道的位置......我实际上偶然找到了答案,试图解决另一个问题。所以,从顶部开始。

从新的“用户”对象开始,我需要首先确保已设置密钥类型。

public class User : IdentityUser<int>
{
}

在我的情况下,因为我想重命名字段以及更改类型,所以完全重新定义它更有意义; Id变为AccountId,使标识符在我的所有表格中保持一致。

public int AccountId { get; set; }

然后在DbContext中备份(在我的情况下为CustomerDbContext),需要在此确认密钥类型更改,这是IdentityDbContext继承的标准更改以指定类型应该使用。

public partial class CustomerDbContext : IdentityDbContext<User, Role, int>
{
}

在此范围内,在base.OnModelCreating(ModelBuilder);之后,我必须定义用于此对象的密钥。

我们也可以在这里更改表名。

protected override void OnModelCreating(ModelBuilder ModelBuilder)
{
    base.OnModelCreating(ModelBuilder);

    ModelBuilder.Entity<User>(Entity =>
    {
        Entity.HasKey(E => E.AccountId);

        Entity.ToTable("Users");
    });
}

Id字段仍会提升其丑陋的头部,但是当我们从IdentityUser中移除我们不想要的字段时,它会被排序。

要重命名NormalizedEmailNormalizedUserName字段,您只需在构建User实体时为其指定列名称,例如: QL( “(GETDATE())”);

Entity.Property(E => E.NormalizedEmail)
    .HasColumnName("NormalisedEmail")
    .IsRequired();

Entity.Property(E => E.NormalizedUserName)
    .HasColumnName("NormalisedUserName");

简单易用,但据我所知,您无法更改列定义,因此,例如,将.HasMaxLength(512)添加到任一列都会使其失效(默认列宽为450) 。这实际上是我问题中第一条错误消息的原因。

  

InvalidOperationException:'Role.NormalisedName'和   'Role.NormalizedName'都映射到'NormalisedName'列中   “角色”但配置为使用不同的数据类型   ('nvarchar(max)'和'nvarchar(256)')。

我发现完全重新定义字段更容易;加入

// I _think_ the original fields are used internally, so I
// couldn't dispense with them entirely.
public string NormalisedEmail
{
    get => NormalizedEmail;
    set => NormalizedEmail = value.ToLowerInvariant();
}
public string NormalisedUserName
{
    get => NormalizedUserName;
    set => NormalizedUserName = value.ToLowerInvariant();
}

到我的User课程,

Entity.Property(E => E.NormalisedEmail)
    .IsRequired()
    .HasMaxLength(512);

Entity.Property(E => E.NormalisedUserName)
    .HasMaxLength(512);

Entity<User>中的CustomerDbContext定义。

最后,删除所有未使用的字段,以及现在重新定义的延迟IdNormalizedEmailNormalizedUserName字段;只需指定ModelBuilder应忽略它们,使用:

Entity.Ignore(E => E.Id)
    .Ignore(E => E.AccessFailedCount)
    .Ignore(E => E.LockoutEnabled)
    .Ignore(E => E.LockoutEnd)
    .Ignore(E => E.NormalizedEmail)
    .Ignore(E => E.NormalizedUserName)
    .Ignore(E => E.PhoneNumber)
    .Ignore(E => E.PhoneNumberConfirmed);

这足以让我先设计我的项目数据库,然后围绕表格弯曲Identity / IdentityServer。