Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/EFCore.MySql/Storage/Internal/MySqlTypeMappingSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class MySqlTypeMappingSource : RelationalTypeMappingSource
// boolean
private readonly MySqlBoolTypeMapping _bit1 = new MySqlBoolTypeMapping("bit", size: 1);
private readonly MySqlBoolTypeMapping _tinyint1 = MySqlBoolTypeMapping.Default;
private readonly MySqlBoolTypeMapping _bool = new MySqlBoolTypeMapping("bool");
private readonly MySqlBoolTypeMapping _boolean = new MySqlBoolTypeMapping("boolean");

// bit
private readonly MySqlULongTypeMapping _bit = new MySqlULongTypeMapping("bit");
Expand Down Expand Up @@ -265,6 +267,11 @@ private void Initialize()

_storeTypeMappings[bit1AsBool ? "bit(1)" : "tinyint(1)"] = new RelationalTypeMapping[] { bit1AsBool ? _bit1 : _tinyint1 };
_clrTypeMappings[typeof(bool)] = bit1AsBool ? _bit1 : _tinyint1;

// BOOL and BOOLEAN are synonyms for TINYINT(1), that do not accept a display width. Without their own mappings, they
// would fall back to the default CLR type mapping and inherit its size postfix (resulting in e.g. `boolean(1)`).
_storeTypeMappings["bool"] = new RelationalTypeMapping[] { _bool };
_storeTypeMappings["boolean"] = new RelationalTypeMapping[] { _boolean };
}

// Guid
Expand Down
108 changes: 108 additions & 0 deletions test/EFCore.MySql.Tests/Migrations/MySqlBooleanStoreTypeAliasTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using Xunit;

namespace Microting.EntityFrameworkCore.MySql.Migrations
{
public sealed class MySqlBooleanStoreTypeAliasTest
: TestWithFixture<MySqlBooleanStoreTypeAliasTest.MySqlBooleanStoreTypeAliasFixture>
{
public MySqlBooleanStoreTypeAliasTest(MySqlBooleanStoreTypeAliasFixture fixture)
: base(fixture)
{
}

[ConditionalTheory]
[InlineData("boolean")]
[InlineData("BOOLEAN")]
[InlineData("bool")]
[InlineData("BOOL")]
public void Boolean_alias_store_type_is_not_suffixed_with_size(string storeType)
{
using var context = Fixture.CreateContext();

var mapping = context.GetService<IRelationalTypeMappingSource>().FindMapping(typeof(bool), storeType);

Assert.NotNull(mapping);
Assert.Equal(storeType, mapping.StoreType);
Assert.Equal(typeof(bool), mapping.ClrType);
}

[ConditionalFact]
public void Default_and_explicit_tinyint_store_types_are_unchanged()
{
using var context = Fixture.CreateContext();
var typeMappingSource = context.GetService<IRelationalTypeMappingSource>();

Assert.Equal("tinyint(1)", typeMappingSource.FindMapping(typeof(bool)).StoreType);
Assert.Equal("tinyint(1)", typeMappingSource.FindMapping(typeof(bool), "tinyint(1)").StoreType);
}

[ConditionalFact]
public void Columns_configured_with_boolean_aliases_keep_the_configured_store_type()
{
using var context = Fixture.CreateContext();

var columns = context.GetService<IDesignTimeModel>().Model
.GetRelationalModel()
.Tables.Single()
.Columns.ToDictionary(c => c.Name, c => c.StoreType);

Assert.Equal("boolean", columns[nameof(Model.Switch.Boolean)]);
Assert.Equal("bool", columns[nameof(Model.Switch.Bool)]);
Assert.Equal("tinyint(1)", columns[nameof(Model.Switch.Default)]);
}

[ConditionalFact]
public void Table_with_boolean_alias_columns_can_be_created_and_round_trips_values()
{
using var context = Fixture.CreateContext();

// The fixture has already created the table. Before the aliases were mapped, that failed with a syntax error near "(1)".
var switches = context.Set<Model.Switch>().OrderBy(s => s.Id).ToList();

Assert.Equal(2, switches.Count);
Assert.True(switches[0].Boolean);
Assert.False(switches[0].Bool);
Assert.False(switches[1].Boolean);
Assert.True(switches[1].Bool);

Assert.Equal(1, context.Set<Model.Switch>().Count(s => s.Boolean && !s.Bool));
}

public class MySqlBooleanStoreTypeAliasFixture
: MySqlTestFixtureBase<MySqlBooleanStoreTypeAliasFixture.MySqlBooleanStoreTypeAliasContext>
{
public class MySqlBooleanStoreTypeAliasContext : ContextBase
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model.Switch>(
entity =>
{
entity.Property(e => e.Boolean).HasColumnType("boolean");
entity.Property(e => e.Bool).HasColumnType("bool");

entity.HasData(
new Model.Switch { Id = 1, Boolean = true, Bool = false, Default = true },
new Model.Switch { Id = 2, Boolean = false, Bool = true, Default = false });
});
}
}
}

private static class Model
{
public class Switch
{
public int Id { get; set; }
public bool Boolean { get; set; }
public bool Bool { get; set; }
public bool Default { get; set; }
}
}
}
}
Loading