Entity Framework 核心字段大小不正确

Entity Framework Core incorrect field size

我正在开发具有 entity framework 核心和 SQL Server 2017 Express Edition 的 dotnet core 2.1 应用程序。我创建了迁移并进行了更新,一切都很顺利,但是,在查询数据库时,我注意到字段的大小没有根据迁移应用。

如何在我的迁移中解决这个问题?

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "Fornecedor",
        columns: table => new
        {
            Id = table.Column<Guid>(nullable: false),
            IdSistemaAntigo = table.Column<string>(type: "varchar", maxLength: 32, nullable: false),
            Status = table.Column<int>(nullable: false),
            DataCadastro = table.Column<DateTime>(nullable: false),
            UltimaMovimentacao = table.Column<DateTime>(nullable: false),
            TipoPessoa = table.Column<int>(nullable: false),
            Nome = table.Column<string>(type: "varchar", maxLength: 100, nullable: false),
            Apelido = table.Column<string>(type: "varchar", maxLength: 100, nullable: true),
            Sexo = table.Column<int>(nullable: true),
            Cnpj = table.Column<string>(type: "varchar", maxLength: 14, nullable: false),
            InscricaoEstadual = table.Column<string>(type: "varchar", maxLength: 15, nullable: false),
            EstadoEmissorInscricaoEstadual = table.Column<string>(type: "varchar", maxLength: 2, nullable: false),
            InscricaoMunicipal = table.Column<string>(type: "varchar", maxLength: 20, nullable: false),
            Cpf = table.Column<string>(type: "varchar", maxLength: 11, nullable: false),
            Rg = table.Column<string>(type: "varchar", maxLength: 15, nullable: false),
            EstadoEmissorRg = table.Column<string>(type: "varchar", maxLength: 2, nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Fornecedor", x => x.Id);
        });

    migrationBuilder.CreateTable(
        name: "FornecedorEmail",
        columns: table => new
        {
            Id = table.Column<Guid>(nullable: false),
            IdSistemaAntigo = table.Column<string>(type: "varchar", maxLength: 32, nullable: false),
            Status = table.Column<int>(nullable: false),
            DataCadastro = table.Column<DateTime>(nullable: false),
            UltimaMovimentacao = table.Column<DateTime>(nullable: false),
            FornecedorId = table.Column<Guid>(nullable: false),
            EnderecoEmail = table.Column<string>(type: "varchar", maxLength: 254, nullable: false),
            NomeExibicao = table.Column<string>(type: "varchar", maxLength: 40, nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_FornecedorEmail", x => x.Id);
            table.ForeignKey(
                name: "FK_FornecedorEmail_Fornecedor_FornecedorId",
                column: x => x.FornecedorId,
                principalTable: "Fornecedor",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateTable(
        name: "FornecedorEndereco",
        columns: table => new
        {
            Id = table.Column<Guid>(nullable: false),
            IdSistemaAntigo = table.Column<string>(type: "varchar", maxLength: 32, nullable: false),
            Status = table.Column<int>(nullable: false),
            DataCadastro = table.Column<DateTime>(nullable: false),
            UltimaMovimentacao = table.Column<DateTime>(nullable: false),
            TipoLogradouro = table.Column<string>(type: "varchar", maxLength: 72, nullable: false),
            Logradouro = table.Column<string>(type: "varchar", maxLength: 72, nullable: false),
            Numero = table.Column<string>(type: "varchar", maxLength: 6, nullable: false),
            Bairro = table.Column<string>(type: "varchar", maxLength: 72, nullable: false),
            Complemento = table.Column<string>(type: "varchar", maxLength: 72, nullable: false),
            Cidade = table.Column<string>(type: "varchar", maxLength: 72, nullable: false),
            Estado = table.Column<string>(type: "varchar", maxLength: 2, nullable: false),
            Pais = table.Column<string>(type: "varchar", maxLength: 2, nullable: false),
            Cep = table.Column<string>(type: "varchar", maxLength: 8, nullable: false),
            CodigoIbge = table.Column<string>(type: "varchar", maxLength: 7, nullable: false),
            Identificador = table.Column<string>(nullable: false),
            FornecedorId = table.Column<Guid>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_FornecedorEndereco", x => x.Id);
            table.ForeignKey(
                name: "FK_FornecedorEndereco_Fornecedor_FornecedorId",
                column: x => x.FornecedorId,
                principalTable: "Fornecedor",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateTable(
        name: "FornecedorTelefone",
        columns: table => new
        {
            Id = table.Column<Guid>(nullable: false),
            IdSistemaAntigo = table.Column<string>(type: "varchar", maxLength: 32, nullable: false),
            Status = table.Column<int>(nullable: false),
            DataCadastro = table.Column<DateTime>(nullable: false),
            UltimaMovimentacao = table.Column<DateTime>(nullable: false),
            Ddi = table.Column<string>(type: "varchar", maxLength: 4, nullable: false),
            Ddd = table.Column<string>(type: "varchar", maxLength: 4, nullable: false),
            Telefone = table.Column<string>(type: "varchar", maxLength: 9, nullable: false),
            Ramal = table.Column<string>(type: "varchar", maxLength: 4, nullable: false),
            TipoTelefone = table.Column<string>(type: "varchar", maxLength: 1, nullable: false),
            Identificador = table.Column<string>(nullable: false),
            FornecedorId = table.Column<Guid>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_FornecedorTelefone", x => x.Id);
            table.ForeignKey(
                name: "FK_FornecedorTelefone_Fornecedor_FornecedorId",
                column: x => x.FornecedorId,
                principalTable: "Fornecedor",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateIndex(
        name: "IX_FornecedorEmail_FornecedorId",
        table: "FornecedorEmail",
        column: "FornecedorId");

    migrationBuilder.CreateIndex(
        name: "IX_FornecedorEndereco_FornecedorId",
        table: "FornecedorEndereco",
        column: "FornecedorId");

    migrationBuilder.CreateIndex(
        name: "IX_FornecedorTelefone_FornecedorId",
        table: "FornecedorTelefone",
        column: "FornecedorId");
}

Migration field size incorrect

这是我的迁移class

        public override void ConfigurarEntidade(EntityTypeBuilder<Fornecedor> builder)
    {
        builder.ToTable("Fornecedor");

        #region Configurações da Entidade

        builder.Property(fornecedor => fornecedor.TipoPessoa)
            .HasColumnType("varchar")
            .HasMaxLength(1)
            .IsRequired();

        builder.Property(fornecedor => fornecedor.Nome)
            .IsRequired()
            .HasColumnType("varchar")
            .HasMaxLength(Fornecedor.TamanhoNome);

        builder.Property(fornecedor => fornecedor.Apelido)
            .HasColumnType("varchar")
            .HasMaxLength(Fornecedor.TamanhoNome);

        builder.Property(fornecedor => fornecedor.Sexo)
            .IsRequired(false);

        builder.OwnsOne(fornecedor => fornecedor.Cnpj, cnpj =>
        {
            cnpj.Property(fornecedor => fornecedor.NumeroCnpj)
                .HasColumnType("varchar")
                .HasColumnName("Cnpj")
                .HasMaxLength(Cnpj.TamanhoCnpj)
                .IsRequired();

        });

        builder.OwnsOne(fornecedor => fornecedor.InscricaoEstadual, ie =>
        {
            ie.Property(fornecedor => fornecedor.NumeroInscricao)
                .HasColumnType("varchar")
                .HasColumnName("InscricaoEstadual")
                .HasMaxLength(InscricaoEstadual.TamanhoInscricaoEstadual)
                .IsRequired();

            ie.Property(fornecedor => fornecedor.EstadoEmissor)
                .HasColumnType("varchar")
                .HasColumnName("EstadoEmissorInscricaoEstadual")
                .HasMaxLength(InscricaoEstadual.TamanhoEstadoEmissor)
                .IsRequired();
        });

        builder.Property(fornecedor => fornecedor.InscricaoMunicipal)
            .HasColumnType("varchar")
            .HasColumnName("InscricaoMunicipal")
            .HasMaxLength(Fornecedor.TamanhoInscricaoMunicipal)
            .IsRequired();

        builder.OwnsOne(fornecedor => fornecedor.Cpf, cpf =>
        {
            cpf.Property(fornecedor => fornecedor.NumeroCpf)
                .HasColumnType("varchar")
                .HasColumnName("Cpf")
                .HasMaxLength(Cpf.TamanhoMaximoCpf)
                .IsRequired();
        });

        builder.OwnsOne(fornecedor => fornecedor.Rg, rg =>
        {
            rg.Property(fornecedor => fornecedor.NumeroRg)
                .HasColumnType("varchar")
                .HasColumnName("Rg")
                .HasMaxLength(Rg.TamanhoRg)
                .IsRequired();

            rg.Property(fornecedor => fornecedor.EstadoEmissor)
                .HasColumnType("varchar")
                .HasColumnName("EstadoEmissorRg")
                .HasMaxLength(Rg.TamanhoEstadoEmissor)
                .IsRequired();
        });


        builder.Ignore(fornecedor => fornecedor.DataNascimento);

        builder.Ignore(fornecedor => fornecedor.Imagem);

        #endregion

        #region Relacionamentos

        builder
            .HasMany(fornecedor => fornecedor.Emails)
            .WithOne(email => email.Fornecedor)
            .HasForeignKey(email => email.FornecedorId);

        builder
            .HasMany(fornecedor => fornecedor.Enderecos)
            .WithOne(endereco => endereco.Fornecedor)
            .HasForeignKey(endereco => endereco.FornecedorId);

        builder
            .HasMany(fornecedor => fornecedor.Telefones)
            .WithOne(telefone => telefone.Fornecedor)
            .HasForeignKey(telefone => telefone.FornecedorId);

        #endregion
    }

这是我的实体

    public class Fornecedor : Entity
{
    public static readonly int TamanhoNome = 100;
    public static readonly int TamanhoApelido = 20;
    public static readonly int TamanhoInscricaoMunicipal = 20;
    public static readonly int TamanhoRg = 20;

    private string _nome;
    private string _nomeFantasia;

    public TipoPessoa TipoPessoa { get; private set; }

    public string Nome
    {
        get { return _nome; }
        private set { _nome = (value == null ? "" : value.RemoverEspacosDuplos().ToCapitalize().Trim()); }
    }

    public string Apelido
    {
        get { return _nomeFantasia; }
        private set { _nomeFantasia = (value == null ? "" : value.RemoverEspacosDuplos().ToCapitalize().Trim()); }
    }

    public Sexo? Sexo { get; private set; }

    public Cnpj Cnpj { get; private set; }

    public InscricaoEstadual InscricaoEstadual { get; private set; }

    public string InscricaoMunicipal { get; private set; }

    public Cpf Cpf { get; private set; }

    public Rg Rg { get; private set; }

    public DateTime? DataNascimento { get; private set; }

    public string Imagem { get; private set; }

    public Fornecedor() { }
}

问题是所有这些 table.Column<string> 调用的 type: "varchar" 参数。提供此参数时,迁移 SQL 生成器会忽略 maxLength 和其他一些参数。因为 type 应该是实际的数据库类型,包括大小和其他约束,例如 varchar(100) 等。当它只是 varchar 时,SqlServer DDL 将其视为 varchar(1).

话虽如此,问题是导致包含该参数的原因。按照惯例,它不包括在内(但映射的数据库类型是 nvarchar(maxLength)),因此必须在模型映射中指定它。

可以通过两种方式完成。

首先是应用 [Column(TypeName ="varchar")] 数据注释。但是,如果这样做,在尝试生成类似于此的迁移时会出现异常

Data type 'varchar' is not supported in this form. Either specify the length explicitly in the type name, for example as 'varchar(16)', or remove the data type and use APIs such as HasMaxLength to allow EF choose the data type.

所以不应该这样,而是使用.HasColumnType("varchar")流利的第二种方式API。当您这样做时,令人惊讶的是,迁移生成时没有任何错误并且包含上述参数。我发现这种行为至少不一致 - 两者都应该抛出或都应该成功。

无论如何,解决方案是将最大长度放在类型名称中,例如

.HasColumnType("varchar(100)")

或尽可能避免 .HasColumnType。例如,string 是映射到 varchar 还是 nvarchar 取决于 isUnicode 属性,默认情况下是 true。因此,要获得所需的 varchar(maxLength) 数据库类型,请替换所有

.HasColumnType("varchar")

在你的 OnModelCreating

.IsUnicode(false)

并让现有的 .HasMaxLenght 调用处理最大长度。并且不要忘记重新生成迁移。

关于 code.IsUnicode(false)code 在我的 codeOnModelCreatingcode 中不起作用。

builder.Property(fornecedor => fornecedor.Nome)
.IsRequired()
.HasColumnType("varchar")
.IsUnicode(false)
.HasMaxLength(Fornecedor.TamanhoNome);

我是这样解决问题的

builder.Property(fornecedor => fornecedor.Nome)
.IsRequired()
.HasColumnType($"varchar({Fornecedor.TamanhoNome})");
//.IsUnicode(false)
//.HasMaxLength(Fornecedor.TamanhoNome);

结果是

IdSistemaAntigo = table.Column<string>(type: "varchar(32)", nullable: false),