jrummell
jrummell

Reputation: 43097

TPH Inheritance mapping uses nchar (N'value') instead of char ('value') for discriminator column value

I'm working with an existing database and using EF 4.3 Code First. I have an entity hierarchy that looks like this, where Note is the base class:

Note
  - CompanyNote
  - OrderNote
  - etc

I'm using TPH with a discriminator column having the following mapping:

Map<CompanyNote>(t => t.Requires("type").HasValue("company"));
Map<OrderNote>(t => t.Requires("type").HasValue("order"));

The database type of type is char(18). EF generates sql as if its nchar:

SELECT /* columns */
FROM [dbo].[notes] AS [Extent1]
WHERE [Extent1].[type] = N'company'

That N is a problem because this table has many thousands of rows and it prevents SQL from using an index. I need to query to look this way:

SELECT /* columns */
FROM [dbo].[notes] AS [Extent1]
WHERE [Extent1].[type] = 'company'

Here's what I've tried so far:

Neither changes made a difference. Unfortunately, I can't change the database column type to nchar.

How can I tell Entity Framework that the discriminator column is of type char?

Update: Here's a complete example

[TestFixture]
public class TphMappingFixture
{
    [Test]
    public void CompanyNotesQueryShouldNotHaveUnicodeDiscriminator()
    {
        string sql;
        using (TphTestContext context = new TphTestContext())
        {
            sql = context.CompanyNotes.ToString();
        }

        Console.WriteLine(sql);

      /* sql is:
        SELECT 
        '0X0X' AS [C1], 
        [Extent1].[id] AS [id], 
        [Extent1].[text] AS [text]
        FROM [dbo].[notes] AS [Extent1]
        WHERE [Extent1].[type] = N'company'
      */

        Assert.That(!sql.Contains("N'company'"));
    }
}

public abstract class TphTestNote
{
    public int Id { get; set; }
    public string Text { get; set; }
}

public class TphTestCompanyNote : TphTestNote
{
}

public class TphTestOrderNote : TphTestNote
{
}

public class TphTestNoteMap : EntityTypeConfiguration<TphTestNote>
{
    public TphTestNoteMap()
    {
        HasKey(t => t.Id);

        Property(t => t.Text)
            .HasMaxLength(254)
            .IsUnicode(false);

        ToTable("notes");

        Property(t => t.Id).HasColumnName("id");
        Property(t => t.Text).HasColumnName("text");

        Map<TphTestCompanyNote>(t => t.Requires("type").HasValue("company").IsUnicode(false));
        Map<TphTestOrderNote>(t => t.Requires("type").HasValue("order").IsUnicode(false));
    }
}

public class TphTestContext : DbContext
{
    static TphTestContext()
    {
        Database.SetInitializer<TphTestContext>(null);
    }

    public DbSet<TphTestCompanyNote> CompanyNotes { get; set; }
    public DbSet<TphTestOrderNote> OrderNotes { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new TphTestNoteMap());
    }
}

Upvotes: 3

Views: 664

Answers (2)

jrummell
jrummell

Reputation: 43097

I'm still not sure why I'm seeing different results than @Slauma, but I finally found something that works for me. I explicitly set the column type to char in the inheritance mapping.

Map<TphTestCompanyNote>(t => t.Requires("type")
                              .HasValue("company")
                              .HasColumnType("char"));

Map<TphTestOrderNote>(t => t.Requires("type")
                            .HasValue("order")
                            .HasColumnType("char"));

And the resulting SQL:

SELECT 
'0X0X' AS [C1], 
[Extent1].[id] AS [id], 
[Extent1].[text] AS [text]
FROM [dbo].[notes] AS [Extent1]
WHERE [Extent1].[type] = 'company'

Upvotes: 3

Slauma
Slauma

Reputation: 177163

I can't reproduce the use of a Unicode string in the SQL query. Test application (Console app with EF 4.3.1):

using System;
using System.Data.Entity;
using System.Linq;

namespace EF43TPH
{
    public abstract class Note
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class CompanyNote : Note
    {
        public string ExtendedName { get; set; }
    }

    public class OrderNote : Note
    {
        public string AnotherExtendedName { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<Note> Notes { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<CompanyNote>()
                .Map<CompanyNote>(t => t.Requires("type").HasValue("company"));

            modelBuilder.Entity<OrderNote>()
                .Map<OrderNote>(t => t.Requires("type").HasValue("order"));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());

            using (var ctx = new MyContext())
            {
                try
                {
                    var query = ctx.Notes.OfType<CompanyNote>();
                    var queryString = query.ToString();
                }
                catch (Exception e)
                {
                    throw;
                }
            }
        }
    }
}

The SQL query I get in queryString is:

SELECT 
'0X0X' AS [C1], 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[ExtendedName] AS [ExtendedName]
FROM [dbo].[Notes] AS [Extent1]
WHERE [Extent1].[type] = 'company'

What is different between this test and your code?

Upvotes: 1

Related Questions