none
Question:Does the DataContext.GetTable support dynamic type? RRS feed

  • Question

  • I used the TypeBuilder to generate a dynamic type of entity class runtime, and used DataContext.GetTable to obtian the table instance, but exception was thrown.

     

    The table in database:

        dbo.temp
        id  int        not null 

        name varchat(50) not null

    The entity class generated by the TypeBuilder

     public class temp {
        private int _id;
        
    private string _name;

        
        
    public int id {
            
    get { return _id; }
            
    set { _id = value; }
        }
        
        
    public string name {
            
    get { return _name; }
            
    set { _name = value; }
        }
    }

     

    The dbml mapping file(temp.map):

     <?xml version="1.0" encoding="utf-8"?>
     <Database Name="temp" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
        <Table Name="dbo.temp" Member="temp">
            <Type Name="temp">
                <Column Name="id" Member="id" Storage="_id" DbType="Int NOT NULL IDENTITY" IsDbGenerated="true" AutoSync="Always" />
                <Column Name="name" Member="name" Storage="_name" DbType="NVarChar(50) NOT NULL" CanBeNull="false" />
            </Type>
        </Table>
    </Database>

     

    Code sample(Mark as italics is the dynamic type of the entity class):

     

    FieldBuilder fb;
    PropertyBuilder pb;
    MethodBuilder mb;
    ILGenerator il;

    AssemblyBuilder asmbuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(AppDomain.CurrentDomain.GetAssemblies()[0
    ].GetName(), AssemblyBuilderAccess.Run);
    ModuleBuilder modbuilder
    = asmbuilder.DefineDynamicModule(AppDomain.CurrentDomain.GetAssemblies()[0].GetModules()[0
    ].Name);
    TypeBuilder tbuilder
    = modbuilder.DefineType("temp", TypeAttributes.Class |
    TypeAttributes.Public);

    fb
    = tbuilder.DefineField("_id", typeof(int
    ), FieldAttributes.Private);
    pb
    = tbuilder.DefineProperty("id", PropertyAttributes.None, typeof(int
    ), Type.EmptyTypes);

    mb
    = tbuilder.DefineMethod("get_id", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(int
    ), Type.EmptyTypes);
    il
    =
    mb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldfld, fb);
    il.Emit(OpCodes.Ret);
    pb.SetGetMethod(mb);

    mb
    = tbuilder.DefineMethod("set_id", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { typeof(int
    ) });
    il
    =
    mb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Stfld, fb);
    il.Emit(OpCodes.Ret);
    pb.SetSetMethod(mb);

    fb
    = tbuilder.DefineField("_name", typeof(string
    ), FieldAttributes.Private);
    pb
    = tbuilder.DefineProperty("name", PropertyAttributes.None, typeof(string
    ), Type.EmptyTypes);

    mb
    = tbuilder.DefineMethod("get_name", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string
    ), Type.EmptyTypes);
    il
    =
    mb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldfld, fb);
    il.Emit(OpCodes.Ret);
    pb.SetGetMethod(mb);

    mb
    = tbuilder.DefineMethod("set_name", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { typeof(string
    ) });
    il
    =
    mb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Stfld, fb);
    il.Emit(OpCodes.Ret);
    pb.SetSetMethod(mb);

    Type temp
    =
    tbuilder.CreateType();

     

    MappingSource map = XmlMappingSource.FromUrl(@"..\..\temp.map");
    tempDataContext db
    = new tempDataContext(Settings.Default.tempConnectionString, map);

    //Call DataContext.GetTable <T>() 

    MethodInfo mi = db.GetType().GetMethod("GetTable", Type.EmptyTypes).MakeGenericMethod(temp);
    mi.Invoke(db,
    null
    );

    /*

    It will throw the exception:

    [System.Reflection.TargetInvocationException] = {"Exception has been thrown by the target of an invocation."}
    InnerException = {"Type must be a type provided by the runtime. Parameter name: type"}

    */

     

     

    //Call DataContext.GetTable(Type)
    db.GetTable(temp);

    /*

    It will throw the exception:

    [System.Reflection.TargetInvocationException] = {"Exception has been thrown by the target of an invocation."}
    InnerException = {"Type must be a type provided by the runtime. Parameter name: type"}

    */

     

    Why the DataContext.GetTable throw these exception?

    How to do DataContext.GetTable could support dynamic type?

    Tuesday, July 15, 2008 4:07 AM

Answers

  • It's likely that the mapping file variation of mapping is not working with the dynamic type. You should try adding the correct mapping attributes to the dynamically generated type; TableAttribute and ColumnAttribute's.

     

    Friday, July 18, 2008 5:31 AM
    Moderator

All replies

  • Thank you for bringing this issue to our attention.

     

    I have been able to reproduce the behavior you are observing. It turns out that the instantiation of the Table<> object having a dynamically-generated type causes the CLR to throw an exception.

     

    I am investigating the issue further, and will post any new information I can gather.

     

    --Samir

     

     

    Tuesday, July 15, 2008 5:59 PM
  •  

    I have a mistake in my posting, DataContext.GetTable<Type>() and DataContext.GetTable(Type) don't throw the same exception.

     

    The exception thrown by DataContext.GetTable(Type) is:

     

    [System.InvalidOperationException] = {"Could not retrieve a Table for inheritance subtype 'temp', try Table of temp instead."}

     

     

     

    Friday, July 18, 2008 5:18 AM
  • It's likely that the mapping file variation of mapping is not working with the dynamic type. You should try adding the correct mapping attributes to the dynamically generated type; TableAttribute and ColumnAttribute's.

     

    Friday, July 18, 2008 5:31 AM
    Moderator
  •  

    It's the same problem, the same exception.

     

    Code Snippet

    ConstructorInfo ci;
    CustomAttributeBuilder attrBuilder;

     

    //apply custom attribute to class

    ci = typeof(TableAttribute).GetConstructor(Type.EmptyTypes);
    attrBuilder = new CustomAttributeBuilder(ci, new object[]{}, new PropertyInfo[]{ typeof(TableAttribute).GetProperty("Name")}, new object[] { "dbo.temp" });
    tbuilder.SetCustomAttribute(attrBuilder);

     

    //apply to property id

    ci = typeof(ColumnAttribute).GetConstructor(Type.EmptyTypes);
    attrBuilder = new CustomAttributeBuilder(ci, new object[] { },
        new PropertyInfo[] {
            typeof(ColumnAttribute).GetProperty("Name"),
            typeof(ColumnAttribute).GetProperty("Storage"),
            typeof(ColumnAttribute).GetProperty("DbType"),
            typeof(ColumnAttribute).GetProperty("IsDbGenerated"),
            typeof(ColumnAttribute).GetProperty("AutoSync")
        },
        new object[] { "id", "_id", "Int NOT NULL IDENTITY", true, AutoSync.Always});
    pb.SetCustomAttribute(attrBuilder);

     

    //apply to property name

    ci = typeof(ColumnAttribute).GetConstructor(Type.EmptyTypes);
    attrBuilder = new CustomAttributeBuilder(ci, new object[] { },
        new PropertyInfo[] {
            typeof(ColumnAttribute).GetProperty("Name"),
            typeof(ColumnAttribute).GetProperty("Storage"),
            typeof(ColumnAttribute).GetProperty("DbType"),
            typeof(ColumnAttribute).GetProperty("CanBeNull")
        },
        new object[] { "name", "_name", "NVarChar(50) NOT NULL", false });
    pb.SetCustomAttribute(attrBuilder);

     

     

    Friday, July 18, 2008 6:49 AM
  • You have to also not use the map file.  If you instantiate the DataContext with an XML mapping file, it will ignore the attributes and try to find the mapping in the file.

     

    Friday, July 18, 2008 4:41 PM
    Moderator
  • It works,thx. But my application need read a few tables dynamically created in runtime according to the project requirements, so I have to use the mapping file. Is this a bug or the limitation of using mapping file? If I can't use the DataContext with the mapping file, it seems I have to make a custom mapping file by myself instead of the mapping file generated by sqlmetal. 

    Friday, July 18, 2008 5:22 PM
  • Any update on making mapping file work with Dynamic types? Is this fixed in .NET 4.0?
    Swami
    Wednesday, April 21, 2010 6:10 PM
  • Update:  I tried this with .NET 4.0 and it works! :)
    Swami
    Friday, April 23, 2010 6:15 PM