Description:
When using Table-Per-Type with a base class and multiple implementations, when this is queried via a join, the generated column C1 from each join to the specific tables clash, causing it to be aliased.
The Discriminator column is generated via a CASE-WHEN construction, but the condition for the base class is incorrectly generated:
CASE WHEN ((NOT ((`Join2`.`C1` = 1) AND (`Join2`.`C1` IS NOT NULL))) AND (NOT ((`Join2`.`C1` = 1) AND (`Join2`.`C1` IS NOT NULL)))) THEN (@gp3) WHEN ((`Join2`.`C1` = 1) AND (`Join2`.`C1` IS NOT NULL)) THEN (@gp4) ELSE (@gp5) END AS `C1`
The condition for @gp3 contains "(NOT ((`Join2`.`C1` = 1) AND (`Join2`.`C1` IS NOT NULL)))" twice, and the ELSE clause returning @gp5 can never be reached.
This has the consequence that the materialized objects will be of the wrong type, typically instance of the base class instead of the subclass. It may also result in an exception (depending on the contents already in the DbContext):
System.NotSupportedException : All objects in the EntitySet 'VehicleDbContext4.Vehicles' must have unique primary keys. However, an instance of type 'MySql.Data.EntityFramework.CodeFirst.Tests.Vehicle4' and an instance of type 'MySql.Data.EntityFramework.CodeFirst.Tests.Car4' both have the same primary key value.
How to repeat:
Defining a model/DbContext like this:
```public class Car4 : Vehicle4
{
public string CarProperty { get; set; }
}
public class Bike4 : Vehicle4
{
public string BikeProperty { get; set; }
}
public class Manufacturer4
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ManufacturerId { get; set; }
public string Name { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid GroupIdentifier { get; set; }
public virtual ICollection<Vehicle4> Vehicles { get; set; }
}
public class Vehicle4
{
public int Id { get; set; }
public int Year { get; set; }
[MaxLength(1024)]
public string Name { get; set; }
public Guid ManufacturerId { get; set; }
[ForeignKey(nameof(ManufacturerId))]
public virtual Manufacturer4 Manufacturer { get; set; }
}
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class VehicleDbContext4 : DbContext
{
public DbSet<Vehicle4> Vehicles { get; set; }
public DbSet<Manufacturer4> Manufacturers { get; set; }
public VehicleDbContext4() : base(CodeFirstFixture.GetEFConnectionString<VehicleDbContext4>())
{
Database.SetInitializer<VehicleDbContext4>(new VehicleDBInitializer4());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Vehicle4>().ToTable("Vehicles");
modelBuilder.Entity<Car4>().ToTable("Cars");
modelBuilder.Entity<Bike4>().ToTable("Bikes");
}
}
public class VehicleDBInitializer4 : DropCreateDatabaseReallyAlways<VehicleDbContext4>
{
}
```
and running this code
```
[Test]
public void BugXXXXX_Test1()
{
#if DEBUG
Debug.WriteLine(new StackTrace().GetFrame(0).GetMethod().Name);
#endif
List<Vehicle4> vehicles;
using (VehicleDbContext4 context = new VehicleDbContext4())
{
context.Database.Delete();
context.Database.Initialize(true);
var manuf = context.Manufacturers.Add(new Manufacturer4 { Name = "ACME" });
context.Vehicles.Add(new Car4 { Id = 1, Name = "Mustang", Year = 2012, CarProperty = "Car", Manufacturer = manuf });
context.Vehicles.Add(new Bike4 { Id = 101, Name = "Mountain", Year = 2011, BikeProperty = "Bike", Manufacturer = manuf });
context.SaveChanges();
// EXCEPTION OCCURS HERE:
vehicles = context.Manufacturers.SelectMany(v => v.Vehicles).ToList();
int records = -1;
using (MySqlConnection conn = new MySqlConnection(context.Database.Connection.ConnectionString))
{
conn.Open();
MySqlCommand cmd = new MySqlCommand("SELECT COUNT(*) FROM Vehicles", conn);
records = Convert.ToInt32(cmd.ExecuteScalar());
}
Assert.AreEqual(context.Vehicles.Count(), records);
}
}```
will result in this exception:
```System.NotSupportedException : All objects in the EntitySet 'VehicleDbContext4.Vehicles' must have unique primary keys. However, an instance of type 'MySql.Data.EntityFramework.CodeFirst.Tests.Vehicle4' and an instance of type 'MySql.Data.EntityFramework.CodeFirst.Tests.Car4' both have the same primary key value. ```
Suggested fix:
Applying the patch
```diff --git a/EntityFramework/src/Statements/SelectStatement.cs b/EntityFramework/src/Statements/SelectStatement.cs
index 96d8ab4e..74e7b2fe 100644
--- a/EntityFramework/src/Statements/SelectStatement.cs
+++ b/EntityFramework/src/Statements/SelectStatement.cs
@@ -232,7 +232,7 @@ namespace MySql.Data.EntityFramework
if (generator.GetTopOp() == OpType.Join)
{
newColumn.ColumnAlias = cf.ColumnAlias;
- newColumn.PushInput(cf.ColumnName);
+ newColumn.PushInput(cf.ActualColumnName);
if (cf.TableName != null)
newColumn.PushInput(cf.TableName);
}
--```
seems to address the issue.