Pomelo.entityframeworkcore.mysql: Fließende API mit Varchar fester Länge

Erstellt am 3. Apr. 2020  ·  3Kommentare  ·  Quelle: PomeloFoundation/Pomelo.EntityFrameworkCore.MySql

Schritte zum Reproduzieren

image

Tag-Modell

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);
    }

Die Angelegenheit

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

image

Es spielt keine Rolle, wie ich die Spalte "Name" mit der Fluet-API definiere, die Einfügung schneidet die Eigenschaft nicht ab. Ich habe versucht:

            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();

das modeldef wird einwandfrei aufgerufen.

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

Weitere technische Details

MySQL-Version:
image

Betriebssystem:
image

Pomelo.EntityFrameworkCore.MySql Version: 3.1.1
Microsoft.AspNetCore.App-Version: .NET Core 3.1

closed-question type-question

Hilfreichster Kommentar

@Toemsel Beachten Sie zusätzlich zu den Aussagen von @mguinness , dass varchar(n) keine feste Länge hat. Es ist ein Speichertyp mit variabler Länge. Das Äquivalent für die feste Länge beträgt jedoch char(n) .

Eine Änderung von varchar(n) in char(n) macht jedoch keinen Unterschied in Bezug auf Ihre Ausnahme.
Dies ist beabsichtigt.

Wenn Sie Ihre Daten zuerst validieren möchten, finden Sie hier einige Artikel dazu:

Wenn Sie nur Zeichenfolgen abschneiden möchten, bevor Sie sie an die Datenbank senden, reicht es möglicherweise aus, einen Wertekonverter zu verwenden , der nur clientseitige Zeichenfolgen abschneidet:

`` `c #
Entität
.Eigenschaft (t => t.Name)
.HasMaxLength (32)
.Erforderlich()
.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");
            }
        }
    }
}

Alle 3 Kommentare

Informationen zur

Entity Framework führt keine Validierung der maximalen Länge durch, bevor Daten an den Anbieter übergeben werden. Es ist Sache des Anbieters oder Datenspeichers, gegebenenfalls zu validieren. Wenn Sie beispielsweise auf SQL Server abzielen, führt das Überschreiten der maximalen Länge zu einer Ausnahme, da der Datentyp der zugrunde liegenden Spalte nicht zulässt, dass überschüssige Daten gespeichert werden.

@Toemsel Beachten Sie zusätzlich zu den Aussagen von @mguinness , dass varchar(n) keine feste Länge hat. Es ist ein Speichertyp mit variabler Länge. Das Äquivalent für die feste Länge beträgt jedoch char(n) .

Eine Änderung von varchar(n) in char(n) macht jedoch keinen Unterschied in Bezug auf Ihre Ausnahme.
Dies ist beabsichtigt.

Wenn Sie Ihre Daten zuerst validieren möchten, finden Sie hier einige Artikel dazu:

Wenn Sie nur Zeichenfolgen abschneiden möchten, bevor Sie sie an die Datenbank senden, reicht es möglicherweise aus, einen Wertekonverter zu verwenden , der nur clientseitige Zeichenfolgen abschneidet:

`` `c #
Entität
.Eigenschaft (t => t.Name)
.HasMaxLength (32)
.Erforderlich()
.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, nun, ich dachte, fließend / Anmerkungen würden nicht nur für die Validierung / Erstellung / Migration existieren. Schön zu wissen und danke für den Hinweis.

@lauxjpn Danke, dass Sie mich über den Wertekonverter informiert haben. Das hilft sehr!

PS: Ja, Varchar ist in meinem Fall von 0 bis 32 dynamisch. Es war eine schlechte Wortwahl.

Danke Jungs!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen