简体   繁体   中英

Entity Framework Code First GenericTypeArguments

I have the unfortunate task to create an MVC project that uses both an MySQL and MSSQL database. To do that I use Entity Framework 6 code first and its working on both databases.

The problem however is that MSSQL supports Schemas and MYSQL does not. If I add the modelBuilder.Entity<Status>().ToTable("Status", schemaName: "Client"); I get an error building the MySQL database.

To solve this I tried adding a custom attribute to all my DbSets to be able to determine the schema I want to use. In MSSQL I will use the schema and in MYSQL I will prefix the table with the schema name.

EG:

MSSQL:
Client.Status
Employee.Status

MYSQL:
Client_Status
Employee_Status

Now to determine the types and schemas I want to use reflection. Unfortunately I cannot use the GenericTypeArguments in the OnModelCreating method. It says 'System.Type' does not contain a definition for 'GenericTypeArguments' and ...

However, if I copy the reflection code to an action in my MVC Controller (seperate project), it does work.

    [TableSchema("Status", "Client")]
    public DbSet<Tables.Status> Status { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //launch debugger to see inspector
        if (System.Diagnostics.Debugger.IsAttached == false)
            System.Diagnostics.Debugger.Launch();

        //use schema or not
        var useSchema = ConfigurationManager.AppSettings["UseSchema"] == "true";

        //get all properties
        PropertyInfo[] properties = typeof(DataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo p in properties)
        {
            // Only work with dbsets
            if (p.PropertyType.Name != "DbSet`1") { continue; }

            //get tableschema attribute
            var attribute = p.GetCustomAttributes(typeof(TableSchema), false).First();


            //the line below reports the error on building
            var type = p.PropertyType.GenericTypeArguments.First().Name;


            //the goal is to use something like 
            modelBuilder.Entity<type>().ToTable(attribute.Table, attribute.Schema);

        }
    }

If I uncomment the line with var type = and enable debugging, I am also able to see the GenericTypeArguments in the inspector.

So how can I use the GenericTypeArguments or something similar to dynamically get the Tables.Status type or an alternative to alter schemas and tablenames dynamically.

UPDATE:

I managed to get the type by casting it to a dynamic first. But then the second part of the code as introduced by Moho fails

            var type = ((dynamic)p).PropertyType.GenericTypeArguments[0];

            if (type != null)
            {
                var t = modelBuilder.GetType();
                var m = t.GetMethod("Entity", BindingFlags.Public);

                //System.NullReferenceException
                var mgm = m.MakeGenericMethod((Type)type);


                var entityTypeConfig = mgm.Invoke(modelBuilder, new object[] { });

                if (!useSchema)
                {
                    entityTypeConfig.GetType()
                        .GetMethods()
                        .Single(mi => mi.Name == "ToTable" && mi.GetParameters().Count()==1)
                        .Invoke(entityTypeConfig, new object[] { attribute.Schema + "_" + attribute.Table });
                }
                else
                {
                    entityTypeConfig.GetType()
                        .GetMethods()
                        .Single(mi => mi.Name == "ToTable" && mi.GetParameters().Count() == 2)
                        .Invoke(entityTypeConfig, new object[] { attribute.Table, attribute.Schema });
                }


            }

You're in the realm of reflection now. Try something like the below:

        var entityTypeConfig = modelBuilder.GetType()
            .GetMethod( "Entity" )
            .MakeGenericMethod( type )
            .Invoke( modelBuilder, new object[] { } );

        entityTypeConfig.GetType()
            .GetMethods()
            .Single( mi => mi.Name == "ToTable" && mi.GetParameters().Count == 2 )
            .Invoke( entityTypeConfig, new object[] { attribute.Table, attribute.Schema } );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM