Tag Model
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);
}
using (var context = new TagContext())
{
await context.Tags.InsertAsync(new Tag() { Name = "asdfasdfasdfasdfasdfadsfasdf;jklasdlasdfafsdlj;kafdsljkfsd;ljkf;sdlj;fkjlsd;jklfds" });
await context.SaveAsync();
}
It doesn't matter how I do define the "name" column with the fluet API, the insert won't trim the property. I did try:
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();
the modeldef gets called flawlessly.
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
MySQL version:
Operating system:
Pomelo.EntityFrameworkCore.MySql version: 3.1.1
Microsoft.AspNetCore.App version: .NET Core 3.1
See maximum length documentation for Entity Properties:
Entity Framework does not do any validation of maximum length before passing data to the provider. It is up to the provider or data store to validate if appropriate. For example, when targeting SQL Server, exceeding the maximum length will result in an exception as the data type of the underlying column will not allow excess data to be stored.
@Toemsel In addition to what @mguinness said, also be aware that varchar(n)
is not of fixed length. It is a variable length store type. It's fixed length equivalent however is char(n)
.
But changing it from varchar(n)
to char(n)
will not make any difference in regards to your exception.
This is by design.
If you want to validate your data first, here are some articles about it:
If you just want to truncate strings before sending them to the database, it might be enough for you to use a Value Converter, that just truncates client-side strings:
```c#
entity
.Property(t => t.Name)
.HasMaxLength(32)
.IsRequired()
.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, well, I thought fluent/annotations would not only exist for validation/creation/migration only. Nice to know and thanks for the hint.
@lauxjpn Thanks for letting me know about the value converter. That helps a lot!
PS: Yes, varchar is dynamic from 0-32 in my case. It was a poor choice of words.
Thank you guys!
Most helpful comment
@Toemsel In addition to what @mguinness said, also be aware that
varchar(n)
is not of fixed length. It is a variable length store type. It's fixed length equivalent however ischar(n)
.But changing it from
varchar(n)
tochar(n)
will not make any difference in regards to your exception.This is by design.
If you want to validate your data first, here are some articles about it:
If you just want to truncate strings before sending them to the database, it might be enough for you to use a Value Converter, that just truncates client-side strings:
```c#
entity
.Property(t => t.Name)
.HasMaxLength(32)
.IsRequired()
.HasConversion(
v => v.Substring(0, Math.Min(v.Length, 32)),
v => v);