Pomelo.entityframeworkcore.mysql: API fluente com varchar de comprimento fixo

Criado em 3 abr. 2020  ·  3Comentários  ·  Fonte: PomeloFoundation/Pomelo.EntityFrameworkCore.MySql

Passos para reproduzir

image

Modelo de Tag

public class Tag : BaseModel
{
    public string Name { get; set; }

    public DateTime Creation { get; set; }

    public int UsageCount { get; set; }

    public int Likes { get; set; }

    public int Dislikes { get; set; }

    public bool IsActive { get; set; }

    public override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Tag>(entity =>
        {
            entity.HasKey(t => t.Id);
            entity.Property(t => t.Id).ValueGeneratedOnAdd();

            entity.Property(t => t.Name).IsFixedLength(true).HasColumnType("VARCHAR(32)").HasMaxLength(32).IsRequired();
            entity.Property(t => t.Creation).HasDefaultValue(DateTime.Now).IsRequired();
            entity.Property(t => t.UsageCount).HasDefaultValue(0).IsRequired();
            entity.Property(t => t.Likes).HasDefaultValue(0).IsRequired();
            entity.Property(t => t.Dislikes).HasDefaultValue(0).IsRequired();
            entity.Property(t => t.IsActive).HasDefaultValue(true).IsRequired();
        });
    }
}

DbContext

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var currentModel in Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof(BaseModel)) && !t.IsAbstract))
            ((BaseModel)Activator.CreateInstance(currentModel)).OnModelCreating(modelBuilder);

        base.OnModelCreating(modelBuilder);
    }

O problema

        using (var context = new TagContext())
        {
            await context.Tags.InsertAsync(new Tag() { Name = "asdfasdfasdfasdfasdfadsfasdf;jklasdlasdfafsdlj;kafdsljkfsd;ljkf;sdlj;fkjlsd;jklfds" });
            await context.SaveAsync();
        }

image

Não importa como eu defino a coluna "nome" com a API fluet, a inserção não cortará a propriedade. Eu tentei:

            entity.Property(t => t.Name).IsFixedLength(true).HasColumnType("VARCHAR(32)").HasMaxLength(32).IsRequired();
            entity.Property(t => t.Name).IsFixedLength(true).HasMaxLength(32).IsRequired();
            entity.Property(t => t.Name).HasMaxLength(32).IsRequired();
            entity.Property(t => t.Name).IsFixedLength(true).HasColumnType("VARCHAR(32)").IsRequired();
            entity.Property(t => t.Name).HasColumnType("VARCHAR(32)").IsRequired();

o modeldef é chamado perfeitamente.

Exception message: Exception thrown: 'Microsoft.EntityFrameworkCore.DbUpdateException' in System.Private.CoreLib.dll
Stack trace:    at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.<ExecuteAsync>d__29.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.<ExecuteAsync>d__8.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.<ExecuteAsync>d__8.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<SaveChangesAsync>d__93.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<SaveChangesAsync>d__97.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.<ExecuteAsync>d__7`2.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__54.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at DBCT.Persistence.Repository.EntityFramework.EntityFrameworkUnitOfWork.<SaveAsync>d__5.MoveNext() in C:\Users\thoma\source\repos\DBCT\DBCT.Persistence\Repository\EntityFramework\EntityFrameworkUnitOfWork.cs:line 22

Mais detalhes técnicos

Versão MySQL:
image

Sistema operacional:
image

Pomelo.EntityFrameworkCore.MySql versão: 3.1.1
Versão Microsoft.AspNetCore.App: .NET Core 3.1

closed-question type-question

Comentários muito úteis

@Toemsel Além do que @mguinness disse, também esteja ciente de que varchar(n) não tem um comprimento fixo. É um tipo de loja de comprimento variável . Seu comprimento fixo equivalente, entretanto, é char(n) .

Mas mudá-lo de varchar(n) para char(n) não fará nenhuma diferença em relação à sua exceção.
Isso ocorre por design.

Se você deseja validar seus dados primeiro, aqui estão alguns artigos sobre isso:

Se você deseja apenas truncar strings antes de enviá-las ao banco de dados, pode ser suficiente usar um conversor de valor , que apenas trunca strings do lado do cliente:

`` `c #
entidade
.Propriedade (t => t.Nome)
.HasMaxLength (32)
.É necessário()
.HasConversion (
v => v.Substring (0, Math.Min (v.Length, 32)),
v => v);


If you want to automate this, based on whether `MaxLength` has been defined or not, you can do that as well. Take a look at the following console app:

```c#
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Storage;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseMySql(
                    "server=127.0.0.1;port=3306;user=root;password=;database=Issue1058",
                    b => b.ServerVersion(new ServerVersion("8.0.20-mysql")))
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>(
                entity =>
                {
                    entity.Property(e => e.Name)
                        .HasMaxLength(5)
                        .IsRequired();
                });

            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                foreach (var property in entityType.GetProperties())
                {
                    var maxLength = property.GetMaxLength().GetValueOrDefault();
                    if (maxLength > 0)
                    {
                        property.SetValueConverter(new ValueConverter<string,string>(
                            v => v.Substring(0, Math.Min(v.Length, maxLength)),
                            v => v));
                    }
                }
            }
        }
    }

    internal class Program
    {
        private static void Main()
        {
            using (var context = new Context())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();
            }

            using (var context = new Context())
            {
                context.IceCreams.Add(new IceCream {Name = "Vanilla"});
                context.SaveChanges();
            }

            using (var context = new Context())
            {
                var iceCreams = context.IceCreams.ToList();

                Debug.Assert(iceCreams.Count == 1);
                Debug.Assert(iceCreams[0].Name == "Vanil");
            }
        }
    }
}

Todos 3 comentários

Consulte a documentação de comprimento máximo para propriedades de entidade :

O Entity Framework não faz nenhuma validação do comprimento máximo antes de passar os dados para o provedor. Cabe ao provedor ou armazenamento de dados validar, se apropriado. Por exemplo, ao direcionar o SQL Server, exceder o comprimento máximo resultará em uma exceção, pois o tipo de dados da coluna subjacente não permitirá que dados em excesso sejam armazenados.

@Toemsel Além do que @mguinness disse, também esteja ciente de que varchar(n) não tem um comprimento fixo. É um tipo de loja de comprimento variável . Seu comprimento fixo equivalente, entretanto, é char(n) .

Mas mudá-lo de varchar(n) para char(n) não fará nenhuma diferença em relação à sua exceção.
Isso ocorre por design.

Se você deseja validar seus dados primeiro, aqui estão alguns artigos sobre isso:

Se você deseja apenas truncar strings antes de enviá-las ao banco de dados, pode ser suficiente usar um conversor de valor , que apenas trunca strings do lado do cliente:

`` `c #
entidade
.Propriedade (t => t.Nome)
.HasMaxLength (32)
.É necessário()
.HasConversion (
v => v.Substring (0, Math.Min (v.Length, 32)),
v => v);


If you want to automate this, based on whether `MaxLength` has been defined or not, you can do that as well. Take a look at the following console app:

```c#
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Storage;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseMySql(
                    "server=127.0.0.1;port=3306;user=root;password=;database=Issue1058",
                    b => b.ServerVersion(new ServerVersion("8.0.20-mysql")))
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>(
                entity =>
                {
                    entity.Property(e => e.Name)
                        .HasMaxLength(5)
                        .IsRequired();
                });

            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                foreach (var property in entityType.GetProperties())
                {
                    var maxLength = property.GetMaxLength().GetValueOrDefault();
                    if (maxLength > 0)
                    {
                        property.SetValueConverter(new ValueConverter<string,string>(
                            v => v.Substring(0, Math.Min(v.Length, maxLength)),
                            v => v));
                    }
                }
            }
        }
    }

    internal class Program
    {
        private static void Main()
        {
            using (var context = new Context())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();
            }

            using (var context = new Context())
            {
                context.IceCreams.Add(new IceCream {Name = "Vanilla"});
                context.SaveChanges();
            }

            using (var context = new Context())
            {
                var iceCreams = context.IceCreams.ToList();

                Debug.Assert(iceCreams.Count == 1);
                Debug.Assert(iceCreams[0].Name == "Vanil");
            }
        }
    }
}

@mguinness oh, bem, pensei que fluentes / anotações não existiriam apenas para validação / criação / migração. É bom saber e obrigado pela dica.

@lauxjpn Obrigado por me informar sobre o conversor de valor. Isso ajuda muito!

PS: Sim, varchar é dinâmico de 0-32 no meu caso. Foi uma má escolha de palavras.

Obrigado pessoal!

Esta página foi útil?
0 / 5 - 0 avaliações