none
EF 4.1: Can't cast to nullable enum type in projection RRS feed

  • Question

  • Hi, here is my sample database:

     

    USE [EFTesting]

    GO

    /****** Object:  Table [dbo].[Schedule]    Script Date: 09/21/2011 11:27:34 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[Schedule](

     [ScheduleID] [uniqueidentifier] NOT NULL,

     [Name] [nvarchar](50) NOT NULL,

     [FrequencyCode] [tinyint] NOT NULL,

     [Version] [timestamp] NOT NULL,

     CONSTRAINT [PK_Schedule_1] PRIMARY KEY CLUSTERED 

    (

     [ScheduleID] ASC

    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

    ) ON [PRIMARY]

    GO

    /****** Object:  Table [dbo].[Customer]    Script Date: 09/21/2011 11:27:34 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[Customer](

     [CustomerID] [uniqueidentifier] NOT NULL,

     [Name] [nvarchar](50) NOT NULL,

     [ScheduleID] [uniqueidentifier] NULL,

     [Version] [timestamp] NOT NULL,

     CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 

    (

     [CustomerID] ASC

    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

    ) ON [PRIMARY]

    GO

    /****** Object:  Default [DF_Schedule_ScheduleID]    Script Date: 09/21/2011 11:27:34 ******/

    ALTER TABLE [dbo].[Schedule] ADD  CONSTRAINT [DF_Schedule_ScheduleID]  DEFAULT (newsequentialid()) FOR [ScheduleID]

    GO

    /****** Object:  Default [DF_Customer_CustomerID]    Script Date: 09/21/2011 11:27:34 ******/

    ALTER TABLE [dbo].[Customer] ADD  CONSTRAINT [DF_Customer_CustomerID]  DEFAULT (newsequentialid()) FOR [CustomerID]

    GO

    /****** Object:  ForeignKey [FK_Customer_Schedule]    Script Date: 09/21/2011 11:27:34 ******/

    ALTER TABLE [dbo].[Customer]  WITH CHECK ADD  CONSTRAINT [FK_Customer_Schedule] FOREIGN KEY([ScheduleID])

    REFERENCES [dbo].[Schedule] ([ScheduleID])

    GO

    ALTER TABLE [dbo].[Customer] CHECK CONSTRAINT [FK_Customer_Schedule]

    GO

     

     

    And here is my sample program which reproduces the error I'm experiencing:

     

     

    using System;
    using System.Data.Entity;
    using System.Linq;
    
    namespace Tester
    {
        public class Context : DbContext
        {
            public Context(string connectionString) : base(connectionString)
            {
                Configuration.LazyLoadingEnabled = false;
                Configuration.ProxyCreationEnabled = false;
            }
            
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                // Customer
                modelBuilder.Entity<Customer>()
                    .HasKey(c => c.ID)
                    .Property(c => c.ID).HasColumnName("CustomerID");
    
                modelBuilder.Entity<Customer>()
                    .Property(c => c.Version).IsConcurrencyToken();
    
                modelBuilder.Entity<Customer>()
                    .HasOptional(c => c.Schedule);
    
                modelBuilder.Entity<Customer>()
                    .ToTable("Customer");
    
                // Schedule
                modelBuilder.Entity<Schedule>()
                    .HasKey(s => s.ID)
                    .Property(s => s.ID).HasColumnName("ScheduleID");
    
                modelBuilder.Entity<Schedule>()
                    .Property(s => s.Version).IsConcurrencyToken();
    
                modelBuilder.Entity<Schedule>()
                    .ToTable("Schedule");
    
                modelBuilder.Entity<Schedule>()
                    .Ignore(s => s.Frequency);
            }
        }
    
        public abstract class Model
        {
            protected Model()
            {
                ID = Guid.NewGuid();
            }
            
            public Guid ID { get; set; }
    
            public byte[] Version { get; set; }
        }
    
        public class Customer : Model
        {
            public string Name { get; set; }
    
            public Guid? ScheduleID { get; set; }
    
            public Schedule Schedule { get; set; }
        }
    
        public class Schedule : Model
        {
            public string Name { get; set; }
    
            public Frequency Frequency
            {
                get { return (Frequency) FrequencyCode; }
                set { FrequencyCode = (byte) value; }
            }
    
            public byte FrequencyCode { get; set; }
        }
    
        public enum Frequency
        {
            Once,
            Daily,
            Weekly,
            Yearly   
        }
    
        public class GridItem
        {
            public Frequency? Frequency { get; set; }
        }
    
        public class Program
        {
            public static void Main(string[] args)
            {
                // clear database
                var context = GetContext();
                context.Database.ExecuteSqlCommand("DELETE FROM Customer");
                context.Database.ExecuteSqlCommand("DELETE FROM Schedule");
    
                // create new customer / schedule
                var customer = new Customer
                {
                    Name = "CUSTOMER",
                    Schedule = new Schedule
                    {
                        Name = "SCHEDULE",
                        Frequency = Frequency.Daily
                    }
                };
    
                context.Set<Customer>().Add(customer);
                context.SaveChanges();
    
                // retrieve grid items
                context = GetContext();
                var query =
                    from c in context.Set<Customer>()
                    select new GridItem
                    {
                        Frequency = (Frequency?) c.Schedule.FrequencyCode
                    };
    
                var list = query.ToList();
            }
    
            private static Context GetContext()
            {
                return new Context(@"Data Source=.\SQLEXPRESS;Initial Catalog=EFTesting;Integrated Security=True;MultipleActiveResultSets=True");
            }
        }
    }
    

     


    When I execute the last query, I get this exception:

    The specified cast from a materialized 'System.Int32' type to a nullable 'Tester.Frequency' type is not valid.

     

    It doesn't matter if the cast is  (Frequency?) c.Schedule.FrequencyCode or (Frequency) c.Schedule.FrequencyCode, I get the same problem, however it works fine if I change the type of the GridItem.Frequency property to Frequency instead of Frequency?.

    Is there a workaround for this problem? I need to cast to a nullable type because the target property is optional and may not always have a value, even though the source entity always has a value in this example.

     

    Any help greatly appreciated!

    Thanks

     


    • Edited by Sam Piper Wednesday, September 21, 2011 1:37 AM
    Wednesday, September 21, 2011 1:35 AM

Answers

  • Hi Sam,

    The behavior you are seeing is a bug in System.Data.Entity.dll. It has been addressed and should be fixed in the next major release. Until then you could try the workaround proposed by Fish. Another workaround would be to create a property of byte (or non-nullable Frequency) type that just sets the value of the Frequency property. You would use this property in the Linq to Entities queries. Something like this:

        public class GridItem
        {
            public Frequency? Frequency { get; set; }
    
            public byte FrequencySetter
            {
                set
                {
                    Frequency = (Frequency)value;
                }
            }
        }
    

    I know this is not pretty but it seems to work and does not require convering anonymous objects to objects of the type you need.

    Pawel



    • Edited by Pawel Kadluczka Thursday, October 6, 2011 11:34 PM
    • Marked as answer by Sam Piper Monday, March 12, 2012 4:55 AM
    Thursday, October 6, 2011 11:33 PM

All replies

  • try to reomve the type and use Annoymous Type:

     var query =
                    from c in context.Set<Customer>()
                    select new 
                    {
                        Frequency = (Frequency?) c.Schedule.FrequencyCode
                    };

                var list = query.ToList();


    I am fish.
    Thursday, September 29, 2011 9:50 AM
  • Hi Sam,

    The behavior you are seeing is a bug in System.Data.Entity.dll. It has been addressed and should be fixed in the next major release. Until then you could try the workaround proposed by Fish. Another workaround would be to create a property of byte (or non-nullable Frequency) type that just sets the value of the Frequency property. You would use this property in the Linq to Entities queries. Something like this:

        public class GridItem
        {
            public Frequency? Frequency { get; set; }
    
            public byte FrequencySetter
            {
                set
                {
                    Frequency = (Frequency)value;
                }
            }
        }
    

    I know this is not pretty but it seems to work and does not require convering anonymous objects to objects of the type you need.

    Pawel



    • Edited by Pawel Kadluczka Thursday, October 6, 2011 11:34 PM
    • Marked as answer by Sam Piper Monday, March 12, 2012 4:55 AM
    Thursday, October 6, 2011 11:33 PM
  • Thanks Pawel, glad that it will be fixed in the next major release - this one keeps cropping up!

    This is a good workaround until then.

    Monday, March 12, 2012 4:56 AM
  • One year later, this bug does not seem to be fixed in version 5.0 of entity framework .... Isn't it ?
    Sunday, October 14, 2012 3:27 PM
  • Have you tried EF5 on .NET Framework 4.5? The fix should have gone to the System.Data.Entity.dll that is part of .NET Framework 4.5.

    Sunday, October 14, 2012 6:35 PM
  • Yes it works now that I run my project from vs2012. But my project is still targeted to .net framework 4.0 !!!

    Why is the bug fixed ??

    Monday, October 15, 2012 10:32 PM