2

Closed

Code First: Column uniquification is not deterministic

description

The following code can produce different models from machine to machine.
class Context : DbContext
{
    public DbSet<TypeBase> Types { get; set; }
}

public class TypeBase
{
    public int Id { get; set; }
}

public class TypeOne : TypeBase
{
    public RelatedType Related { get; set; }
}

public class TypeTwo : TypeBase
{
    public RelatedType Related { get; set; }
}

public class RelatedType
{
    public int Id { get; set; }
}
Generating a migration between the two different models yields the following code.
RenameColumn(table: "Types", name: "Related_Id", newName: "Related_Id1");
RenameColumn(table: "Types", name: "Related_Id1", newName: "Related_Id");
As you can see, the column that gets a "1" appended is not consistent between the models.
Closed Jul 25, 2013 at 12:50 AM by maumar
Verified, closing

comments

RoMiller wrote Jan 18, 2013 at 5:27 PM

EF Team Triage: We need to be careful when making these changes - we should try and make the most common outcome become the deterministic outcome. This will cause less 'outdated view gen' issues.

BriceLambson wrote Mar 8, 2013 at 6:02 PM

This relates to Work Item 816.

maumar wrote Jul 17, 2013 at 10:28 PM

Current fix introduces data corruption for certain scenarios. Basically if one creates the following model using EF5:
    public class Context : DbContext
    {
        public DbSet<TypeBase> Types { get; set; }
        public DbSet<RelatedType> Related { get; set; }
    }

    public class TypeBase
    {
        public int Id { get; set; }
    }

    public class TypeTwo : TypeBase
    {
        public virtual RelatedType Related { get; set; }
    }

    public class TypeOne : TypeBase
    {
        public virtual RelatedType Related { get; set; }
    }

    public class RelatedType
    {
        public int Id { get; set; }
    }
Then fill it with the following data:
            using (var ctx = new Context())
            {
                var rt1 = new RelatedType();

                var t1 = new TypeOne
                {
                    Related = rt1,
                };

                ctx.Types.Add(t1);
                ctx.Related.Add(rt1);
                ctx.SaveChanges();
            }
And issue the following query:
ctx.Types.OfType<TypeOne>().Select(t => t.Related.Id).ToList();
you would expect to see the one result with value 1.

Now, if we update the code to EF6 with the fix, and issue the query again we see the exception. Basially t.Related is null, rather than entity with Id=1. The reason is, that now we sort types based on their type names alphabetically, rather than via Reflection ordering. In this case it creates a different edmx, where TypeOne and TypeTwo orders are swapped. This in turn swaps names of Related_Id and Related_Id1, and therefore corrupting the references.

emilcicos wrote Jul 23, 2013 at 9:21 PM

The data corruption was caused by a regression of a different bug fix. That should be fixed now.