Executing an IQueryable after using Cast<T> throws Exception

Discussion of open issues, suggestions and bugs regarding LinqConnect – Devart's LINQ to SQL compatible ORM
Post Reply
freakshow
Posts: 12
Joined: Mon 28 Jul 2008 12:38
Location: Oslo, Norway

Executing an IQueryable after using Cast<T> throws Exception

Post by freakshow » Wed 29 Oct 2008 13:27

This is related to Linq to Oracle, 5.0 beta dotConnect

After trying to cast ITable to an IQueryable, various exceptions are thrown.

I have created a complete console app that reproduces the two different exceptions I get when trying two different approaches.

Code: Select all

using System;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Devart.Data.Linq;

namespace DevArtLinqToOracleExceptions
{

    /* 
    1. Create a console application
    2. Make sure project references DevArt.Data.Linq, DevArt.Data.Oracle, etc...
    3. Change connectionstring below
    4. Run Oracle create script for test table:
     
    CREATE TABLE EMPLOYEE ("ID" NUMBER NOT NULL ENABLE, "NAME" NVARCHAR2(50));
    INSERT INTO EMPLOYEE (ID, NAME) VALUES (1, 'John Johnson');
    INSERT INTO EMPLOYEE (ID, NAME) VALUES (2, 'Jane Doe');
    COMMIT;

    
     */
   
    class Program
    {
        static void Main(string[] args)
        {

            using (EntityContext ctx = new EntityContext())
            {

                // All the scenarios below works with Linq to Sql, 
                // but only scenario 1 works with Linq to Oracle

                // Scenario 1: Works

                var firstEmployee = (from emp in ctx.GetTable()
                          select emp).First();

                Console.WriteLine(firstEmployee.Id);


                // Scenario 2: Does not work, throws NotSupportedException

                ITable table1 = ctx.GetTable(typeof(Employee));
                IQueryable empTable1 = table1.WorkaroundCast();

                var qry = from e in empTable1
                          select e;

                foreach (IEmployee employee in qry)  // Throws NotSupportedException here
                {
                    Console.WriteLine(employee.Id);
                    break;
                }

                // Scenario 3: Does not work, throw NullReferenceException

                ITable table = ctx.GetTable(typeof (Employee));
                IQueryable empTable = table.WorkaroundCast();

                foreach (Employee employee in empTable) // throws NullReferenceException here
                {
                    Console.WriteLine(employee.Id);
                    break;
                }
            }

            Console.ReadLine();
        }
    }

    [Provider(typeof(Devart.Data.Oracle.Linq.Provider.OracleDataProvider))]
    public class EntityContext : Devart.Data.Linq.DataContext
    {
        public EntityContext()
            : base("User Id=xx;Password=xx;Server=xx;Direct=True;Sid=xx") { }
    }

    public interface IEmployee
    {
        int Id { get; set; }
        string Name { get; set; }
    }

    [Table(Name = "EMPLOYEE")]
    public class Employee : IEmployee
    {
        [Column(Name = "ID", IsPrimaryKey = true)]
        public int Id { get; set; }

        [Column(Name = "NAME")]
        public string Name { get; set; }
    }

    public static class WorkaroundClass
    {
        // Workaround for NotImplemented exception as I have posted here: 
        // http://devart.com/forums/viewtopic.php?t=13237
        public static IQueryable WorkaroundCast(this IQueryable source)
        {
            MethodInfo method = ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TResult) });
            Expression[] expressions = new Expression[] { source.Expression };
            MethodCallExpression callExpression = Expression.Call(null, method, expressions);

            // CreateQuery(Expression) call is the one that is not implemented, so we use CreateQuery(Expression) instead
            IQueryable query = source.Provider.CreateQuery(callExpression);
            return query;
        }
    }

}
Also see the related post for why I am using a workaround Cast method:
http://devart.com/forums/viewtopic.php?t=13237

In addition to these issues beeing fixed, we really need to know if you intend to implement all features that are available to Linq to Sql in Linq to Oracle?

Freddy Hansen

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Post by Shalex » Thu 30 Oct 2008 15:47

Thank you for the bug report. We will investigate this issue and notify you on the results as soon as possible.

freakshow
Posts: 12
Joined: Mon 28 Jul 2008 12:38
Location: Oslo, Norway

Get different exception after relase

Post by freakshow » Mon 17 Nov 2008 08:35

Hi!

I have now tested this against the release of dotConnect. Scenario 3 works, but scenario 2 still throws an exception. However, this is a different exception.

Code: Select all

Object of type 'System.Func`2[System.Data.IDataRecord,DevArtLinqToOracleExceptions.Employee]' cannot be converted to type 'System.Func`2[System.Data.IDataRecord,DevArtLinqToOracleExceptions.IEmployee]'.


   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Devart.Data.Linq.Provider.c.a(Type A_0, SqlNode A_1, d A_2, m A_3)
   at Devart.Data.Linq.Provider.DataProvider.a(Expression A_0)
   at Devart.Data.Linq.Provider.DataProvider.h(Expression A_0)
   at Devart.Data.Linq.DataQuery`1.i()
   at DevArtLinqToOracleExceptions.Program.Main(String[] args) in g:\tmp\DevartTest\ConsoleApplication1\Program.cs:line 53
   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

This is the example from the first post - only modified to work with the release and not the "Cast" workaround:

Code: Select all

using System;
using System.Data.Linq.Mapping;
using System.Linq;
using Devart.Data.Linq;

namespace DevArtLinqToOracleExceptions
{

    /* 
    1. Create a console application 
    2. Make sure project references DevArt.Data.Linq, DevArt.Data.Oracle, etc... 
    3. Change connectionstring below 
    4. Run Oracle create script for test table: 
      
    CREATE TABLE EMPLOYEE ("ID" NUMBER NOT NULL ENABLE, "NAME" NVARCHAR2(50)); 
    INSERT INTO EMPLOYEE (ID, NAME) VALUES (1, 'John Johnson'); 
    INSERT INTO EMPLOYEE (ID, NAME) VALUES (2, 'Jane Doe'); 
    COMMIT; 

    
     */

    class Program
    {
        static void Main(string[] args)
        {
            using (EntityContext ctx = new EntityContext())
            {

                // All the scenarios below works with Linq to Sql, 
                // but only scenario 1 works with Linq to Oracle 

                // Scenario 1: Works 

                var firstEmployee = (from emp in ctx.GetTable()
                                     select emp).First();

                Console.WriteLine(firstEmployee.Id);


                // Scenario 2: Does still not work, throws:
                // Object of type 'System.Func`2[System.Data.IDataRecord,DevArtLinqToOracleExceptions.Employee]' 
                // cannot be converted to type 'System.Func`2[System.Data.IDataRecord,DevArtLinqToOracleExceptions.IEmployee]'.

                ITable table1 = ctx.GetTable(typeof(Employee));
                IQueryable empTable1 = table1.Cast();

                var qry = from e in empTable1
                          select e;

                foreach (IEmployee employee in qry)  // Throws exception here 
                {
                    Console.WriteLine(employee.Id);
                    break;
                }


                // Scenario 3: Works now in final release

                ITable table2 = ctx.GetTable(typeof(Employee));
                IQueryable empTable2 = table2.Cast();

                foreach (Employee employee in empTable2)
                {
                    Console.WriteLine(employee.Id);
                    break;
                }
            }

            Console.ReadLine();
        }
    }

    [Provider(typeof(Devart.Data.Oracle.Linq.Provider.OracleDataProvider))]
    public class EntityContext : Devart.Data.Linq.DataContext
    {
        public EntityContext()
            : base("User Id=xx;Password=xx;Server=xx;Direct=True;Sid=xx") { }
    }

    public interface IEmployee
    {
        int Id { get; set; }
        string Name { get; set; }
    }

    [Table(Name = "EMPLOYEE")]
    public class Employee : IEmployee
    {
        [Column(Name = "ID", IsPrimaryKey = true)]
        public int Id { get; set; }

        [Column(Name = "NAME")]
        public string Name { get; set; }
    }
}

AndreyR
Devart Team
Posts: 2919
Joined: Mon 07 Jul 2008 13:16

Post by AndreyR » Wed 19 Nov 2008 13:35

Thank you for the report. The problem is solved, look forward to the next build.

Post Reply